@aglyn/plugins-ai 1.0.0-beta.143
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/LICENSE +201 -0
- package/README.md +25 -0
- package/package.json +60 -0
- package/src/index.d.ts +21 -0
- package/src/index.js +22 -0
- package/src/index.js.map +1 -0
- package/src/lib/activity/ai-activity-actions.d.ts +106 -0
- package/src/lib/activity/ai-activity-actions.js +209 -0
- package/src/lib/activity/ai-activity-actions.js.map +1 -0
- package/src/lib/activity/ai-activity.d.ts +219 -0
- package/src/lib/activity/ai-activity.js +251 -0
- package/src/lib/activity/ai-activity.js.map +1 -0
- package/src/lib/billing/ai-overage-charge.d.ts +102 -0
- package/src/lib/billing/ai-overage-charge.js +258 -0
- package/src/lib/billing/ai-overage-charge.js.map +1 -0
- package/src/lib/billing/ai-overage-close.d.ts +62 -0
- package/src/lib/billing/ai-overage-close.js +129 -0
- package/src/lib/billing/ai-overage-close.js.map +1 -0
- package/src/lib/billing/ai-overage-cutover.d.ts +63 -0
- package/src/lib/billing/ai-overage-cutover.js +114 -0
- package/src/lib/billing/ai-overage-cutover.js.map +1 -0
- package/src/lib/billing/ai-overage-events.d.ts +77 -0
- package/src/lib/billing/ai-overage-events.js +174 -0
- package/src/lib/billing/ai-overage-events.js.map +1 -0
- package/src/lib/billing/ai-overage-gate.d.ts +147 -0
- package/src/lib/billing/ai-overage-gate.js +177 -0
- package/src/lib/billing/ai-overage-gate.js.map +1 -0
- package/src/lib/billing/ai-overage-ledger.d.ts +135 -0
- package/src/lib/billing/ai-overage-ledger.js +278 -0
- package/src/lib/billing/ai-overage-ledger.js.map +1 -0
- package/src/lib/billing/ai-overage-standing-writes.d.ts +110 -0
- package/src/lib/billing/ai-overage-standing-writes.js +205 -0
- package/src/lib/billing/ai-overage-standing-writes.js.map +1 -0
- package/src/lib/billing/ai-overage-standing.d.ts +168 -0
- package/src/lib/billing/ai-overage-standing.js +215 -0
- package/src/lib/billing/ai-overage-standing.js.map +1 -0
- package/src/lib/billing/ai-overage-trigger.d.ts +30 -0
- package/src/lib/billing/ai-overage-trigger.js +76 -0
- package/src/lib/billing/ai-overage-trigger.js.map +1 -0
- package/src/lib/components/ai-allotment-editor.component.d.ts +40 -0
- package/src/lib/components/ai-allotment-editor.component.js +213 -0
- package/src/lib/components/ai-allotment-editor.component.js.map +1 -0
- package/src/lib/components/ai-assist-actions-context.d.ts +13 -0
- package/src/lib/components/ai-assist-actions-context.js +21 -0
- package/src/lib/components/ai-assist-actions-context.js.map +1 -0
- package/src/lib/components/ai-assist-provider.component.d.ts +51 -0
- package/src/lib/components/ai-assist-provider.component.js +519 -0
- package/src/lib/components/ai-assist-provider.component.js.map +1 -0
- package/src/lib/components/ai-brief-dialog.component.d.ts +56 -0
- package/src/lib/components/ai-brief-dialog.component.js +319 -0
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -0
- package/src/lib/components/ai-collaborator-permissions-column.component.d.ts +32 -0
- package/src/lib/components/ai-collaborator-permissions-column.component.js +132 -0
- package/src/lib/components/ai-collaborator-permissions-column.component.js.map +1 -0
- package/src/lib/components/ai-credits-card.component.d.ts +34 -0
- package/src/lib/components/ai-credits-card.component.js +131 -0
- package/src/lib/components/ai-credits-card.component.js.map +1 -0
- package/src/lib/components/ai-credits-columns.component.d.ts +56 -0
- package/src/lib/components/ai-credits-columns.component.js +118 -0
- package/src/lib/components/ai-credits-columns.component.js.map +1 -0
- package/src/lib/components/ai-crm-email-draft.component.d.ts +17 -0
- package/src/lib/components/ai-crm-email-draft.component.js +159 -0
- package/src/lib/components/ai-crm-email-draft.component.js.map +1 -0
- package/src/lib/components/ai-crm-import-mapping.component.d.ts +20 -0
- package/src/lib/components/ai-crm-import-mapping.component.js +167 -0
- package/src/lib/components/ai-crm-import-mapping.component.js.map +1 -0
- package/src/lib/components/ai-crm-record-card.component.d.ts +27 -0
- package/src/lib/components/ai-crm-record-card.component.js +263 -0
- package/src/lib/components/ai-crm-record-card.component.js.map +1 -0
- package/src/lib/components/ai-describe-automation.component.d.ts +31 -0
- package/src/lib/components/ai-describe-automation.component.js +226 -0
- package/src/lib/components/ai-describe-automation.component.js.map +1 -0
- package/src/lib/components/ai-describe-button.component.d.ts +39 -0
- package/src/lib/components/ai-describe-button.component.js +87 -0
- package/src/lib/components/ai-describe-button.component.js.map +1 -0
- package/src/lib/components/ai-describe-page.component.d.ts +26 -0
- package/src/lib/components/ai-describe-page.component.js +18 -0
- package/src/lib/components/ai-describe-page.component.js.map +1 -0
- package/src/lib/components/ai-experiment-cards.component.d.ts +75 -0
- package/src/lib/components/ai-experiment-cards.component.js +573 -0
- package/src/lib/components/ai-experiment-cards.component.js.map +1 -0
- package/src/lib/components/ai-explain-automation.component.d.ts +31 -0
- package/src/lib/components/ai-explain-automation.component.js +265 -0
- package/src/lib/components/ai-explain-automation.component.js.map +1 -0
- package/src/lib/components/ai-insight-dialog.component.d.ts +37 -0
- package/src/lib/components/ai-insight-dialog.component.js +441 -0
- package/src/lib/components/ai-insight-dialog.component.js.map +1 -0
- package/src/lib/components/ai-job-plan.component.d.ts +46 -0
- package/src/lib/components/ai-job-plan.component.js +192 -0
- package/src/lib/components/ai-job-plan.component.js.map +1 -0
- package/src/lib/components/ai-model-selector.component.d.ts +21 -0
- package/src/lib/components/ai-model-selector.component.js +109 -0
- package/src/lib/components/ai-model-selector.component.js.map +1 -0
- package/src/lib/components/ai-page-brief-dialog.component.d.ts +29 -0
- package/src/lib/components/ai-page-brief-dialog.component.js +27 -0
- package/src/lib/components/ai-page-brief-dialog.component.js.map +1 -0
- package/src/lib/components/ai-permissions-on-host.component.d.ts +55 -0
- package/src/lib/components/ai-permissions-on-host.component.js +91 -0
- package/src/lib/components/ai-permissions-on-host.component.js.map +1 -0
- package/src/lib/components/ai-product-copy-card.component.d.ts +25 -0
- package/src/lib/components/ai-product-copy-card.component.js +328 -0
- package/src/lib/components/ai-product-copy-card.component.js.map +1 -0
- package/src/lib/components/ai-product-import-option.component.d.ts +14 -0
- package/src/lib/components/ai-product-import-option.component.js +58 -0
- package/src/lib/components/ai-product-import-option.component.js.map +1 -0
- package/src/lib/components/ai-products-hub-card.component.d.ts +15 -0
- package/src/lib/components/ai-products-hub-card.component.js +418 -0
- package/src/lib/components/ai-products-hub-card.component.js.map +1 -0
- package/src/lib/components/ai-products-proposals.component.d.ts +19 -0
- package/src/lib/components/ai-products-proposals.component.js +547 -0
- package/src/lib/components/ai-products-proposals.component.js.map +1 -0
- package/src/lib/components/ai-save-as-component.component.d.ts +38 -0
- package/src/lib/components/ai-save-as-component.component.js +231 -0
- package/src/lib/components/ai-save-as-component.component.js.map +1 -0
- package/src/lib/components/ai-seo-audit-card.component.d.ts +9 -0
- package/src/lib/components/ai-seo-audit-card.component.js +620 -0
- package/src/lib/components/ai-seo-audit-card.component.js.map +1 -0
- package/src/lib/components/ai-seo-fields-card.component.d.ts +10 -0
- package/src/lib/components/ai-seo-fields-card.component.js +453 -0
- package/src/lib/components/ai-seo-fields-card.component.js.map +1 -0
- package/src/lib/components/ai-site-batch-card.component.d.ts +32 -0
- package/src/lib/components/ai-site-batch-card.component.js +531 -0
- package/src/lib/components/ai-site-batch-card.component.js.map +1 -0
- package/src/lib/components/ai-site-seo-start-card.component.d.ts +26 -0
- package/src/lib/components/ai-site-seo-start-card.component.js +178 -0
- package/src/lib/components/ai-site-seo-start-card.component.js.map +1 -0
- package/src/lib/components/ai-site-start-card.component.d.ts +19 -0
- package/src/lib/components/ai-site-start-card.component.js +348 -0
- package/src/lib/components/ai-site-start-card.component.js.map +1 -0
- package/src/lib/components/ai-template-collection-field.component.d.ts +32 -0
- package/src/lib/components/ai-template-collection-field.component.js +63 -0
- package/src/lib/components/ai-template-collection-field.component.js.map +1 -0
- package/src/lib/components/ai-theme-proposal-card.component.d.ts +6 -0
- package/src/lib/components/ai-theme-proposal-card.component.js +619 -0
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -0
- package/src/lib/components/ai-usage-strip.component.d.ts +27 -0
- package/src/lib/components/ai-usage-strip.component.js +145 -0
- package/src/lib/components/ai-usage-strip.component.js.map +1 -0
- package/src/lib/components/assist-edit-canvas.d.ts +169 -0
- package/src/lib/components/assist-edit-canvas.js +636 -0
- package/src/lib/components/assist-edit-canvas.js.map +1 -0
- package/src/lib/components/assist-edit-card.component.d.ts +16 -0
- package/src/lib/components/assist-edit-card.component.js +181 -0
- package/src/lib/components/assist-edit-card.component.js.map +1 -0
- package/src/lib/components/assist-jobs-drawer.component.d.ts +72 -0
- package/src/lib/components/assist-jobs-drawer.component.js +626 -0
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -0
- package/src/lib/components/assist-panel.component.d.ts +136 -0
- package/src/lib/components/assist-panel.component.js +1099 -0
- package/src/lib/components/assist-panel.component.js.map +1 -0
- package/src/lib/components/assist-signals-page.component.d.ts +3 -0
- package/src/lib/components/assist-signals-page.component.js +1017 -0
- package/src/lib/components/assist-signals-page.component.js.map +1 -0
- package/src/lib/components/besigner-ai-controls.component.d.ts +29 -0
- package/src/lib/components/besigner-ai-controls.component.js +81 -0
- package/src/lib/components/besigner-ai-controls.component.js.map +1 -0
- package/src/lib/components/billing-ai-allotments.component.d.ts +36 -0
- package/src/lib/components/billing-ai-allotments.component.js +486 -0
- package/src/lib/components/billing-ai-allotments.component.js.map +1 -0
- package/src/lib/components/billing-ai-top-users.component.d.ts +44 -0
- package/src/lib/components/billing-ai-top-users.component.js +304 -0
- package/src/lib/components/billing-ai-top-users.component.js.map +1 -0
- package/src/lib/components/billing-assist-overage-card.component.d.ts +69 -0
- package/src/lib/components/billing-assist-overage-card.component.js +437 -0
- package/src/lib/components/billing-assist-overage-card.component.js.map +1 -0
- package/src/lib/components/host-ai-allotments.component.d.ts +40 -0
- package/src/lib/components/host-ai-allotments.component.js +239 -0
- package/src/lib/components/host-ai-allotments.component.js.map +1 -0
- package/src/lib/components/member-ai-allotment-card.component.d.ts +28 -0
- package/src/lib/components/member-ai-allotment-card.component.js +172 -0
- package/src/lib/components/member-ai-allotment-card.component.js.map +1 -0
- package/src/lib/components/member-ai-usage-card.component.d.ts +28 -0
- package/src/lib/components/member-ai-usage-card.component.js +240 -0
- package/src/lib/components/member-ai-usage-card.component.js.map +1 -0
- package/src/lib/components/staff-org-ai-card.component.d.ts +17 -0
- package/src/lib/components/staff-org-ai-card.component.js +739 -0
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -0
- package/src/lib/components/staff-org-usage-ai-columns.component.d.ts +41 -0
- package/src/lib/components/staff-org-usage-ai-columns.component.js +65 -0
- package/src/lib/components/staff-org-usage-ai-columns.component.js.map +1 -0
- package/src/lib/components/staff-orgs-ai-spend-column.component.d.ts +28 -0
- package/src/lib/components/staff-orgs-ai-spend-column.component.js +89 -0
- package/src/lib/components/staff-orgs-ai-spend-column.component.js.map +1 -0
- package/src/lib/components/staff-user-ai-usage-card.component.d.ts +27 -0
- package/src/lib/components/staff-user-ai-usage-card.component.js +183 -0
- package/src/lib/components/staff-user-ai-usage-card.component.js.map +1 -0
- package/src/lib/components/use-ai-allotments.d.ts +49 -0
- package/src/lib/components/use-ai-allotments.js +171 -0
- package/src/lib/components/use-ai-allotments.js.map +1 -0
- package/src/lib/components/use-ai-crm-answer.d.ts +50 -0
- package/src/lib/components/use-ai-crm-answer.js +220 -0
- package/src/lib/components/use-ai-crm-answer.js.map +1 -0
- package/src/lib/components/use-ai-job-run.d.ts +51 -0
- package/src/lib/components/use-ai-job-run.js +176 -0
- package/src/lib/components/use-ai-job-run.js.map +1 -0
- package/src/lib/components/use-ai-model-choice.d.ts +47 -0
- package/src/lib/components/use-ai-model-choice.js +150 -0
- package/src/lib/components/use-ai-model-choice.js.map +1 -0
- package/src/lib/components/use-ai-products-jobs.d.ts +36 -0
- package/src/lib/components/use-ai-products-jobs.js +190 -0
- package/src/lib/components/use-ai-products-jobs.js.map +1 -0
- package/src/lib/components/use-ai-usage-meter.d.ts +11 -0
- package/src/lib/components/use-ai-usage-meter.js +96 -0
- package/src/lib/components/use-ai-usage-meter.js.map +1 -0
- package/src/lib/components/use-org-ai-usage.d.ts +42 -0
- package/src/lib/components/use-org-ai-usage.js +181 -0
- package/src/lib/components/use-org-ai-usage.js.map +1 -0
- package/src/lib/components/use-staff-orgs-ai-spend.d.ts +18 -0
- package/src/lib/components/use-staff-orgs-ai-spend.js +127 -0
- package/src/lib/components/use-staff-orgs-ai-spend.js.map +1 -0
- package/src/lib/constants.d.ts +24 -0
- package/src/lib/constants.js +23 -0
- package/src/lib/constants.js.map +1 -0
- package/src/lib/declarations.d.ts +48 -0
- package/src/lib/declarations.js +129 -0
- package/src/lib/declarations.js.map +1 -0
- package/src/lib/declarations.server.d.ts +35 -0
- package/src/lib/declarations.server.js +138 -0
- package/src/lib/declarations.server.js.map +1 -0
- package/src/lib/insights/ai-figure-readers.d.ts +90 -0
- package/src/lib/insights/ai-figure-readers.js +863 -0
- package/src/lib/insights/ai-figure-readers.js.map +1 -0
- package/src/lib/insights/ai-insight-digest.d.ts +165 -0
- package/src/lib/insights/ai-insight-digest.js +363 -0
- package/src/lib/insights/ai-insight-digest.js.map +1 -0
- package/src/lib/insights/ai-insight-readers.d.ts +71 -0
- package/src/lib/insights/ai-insight-readers.js +141 -0
- package/src/lib/insights/ai-insight-readers.js.map +1 -0
- package/src/lib/jobs/ai-crm-access.d.ts +61 -0
- package/src/lib/jobs/ai-crm-access.js +94 -0
- package/src/lib/jobs/ai-crm-access.js.map +1 -0
- package/src/lib/jobs/ai-email-bindings.d.ts +97 -0
- package/src/lib/jobs/ai-email-bindings.js +147 -0
- package/src/lib/jobs/ai-email-bindings.js.map +1 -0
- package/src/lib/jobs/ai-job-admission.d.ts +82 -0
- package/src/lib/jobs/ai-job-admission.js +45 -0
- package/src/lib/jobs/ai-job-admission.js.map +1 -0
- package/src/lib/jobs/ai-job-budget.d.ts +167 -0
- package/src/lib/jobs/ai-job-budget.js +161 -0
- package/src/lib/jobs/ai-job-budget.js.map +1 -0
- package/src/lib/jobs/ai-job-campaign-step.d.ts +108 -0
- package/src/lib/jobs/ai-job-campaign-step.js +340 -0
- package/src/lib/jobs/ai-job-campaign-step.js.map +1 -0
- package/src/lib/jobs/ai-job-component-checks.d.ts +74 -0
- package/src/lib/jobs/ai-job-component-checks.js +442 -0
- package/src/lib/jobs/ai-job-component-checks.js.map +1 -0
- package/src/lib/jobs/ai-job-component-step.d.ts +75 -0
- package/src/lib/jobs/ai-job-component-step.js +262 -0
- package/src/lib/jobs/ai-job-component-step.js.map +1 -0
- package/src/lib/jobs/ai-job-crm-step.d.ts +192 -0
- package/src/lib/jobs/ai-job-crm-step.js +734 -0
- package/src/lib/jobs/ai-job-crm-step.js.map +1 -0
- package/src/lib/jobs/ai-job-draft-ids.d.ts +62 -0
- package/src/lib/jobs/ai-job-draft-ids.js +132 -0
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -0
- package/src/lib/jobs/ai-job-drafts.d.ts +304 -0
- package/src/lib/jobs/ai-job-drafts.js +557 -0
- package/src/lib/jobs/ai-job-drafts.js.map +1 -0
- package/src/lib/jobs/ai-job-email-step.d.ts +195 -0
- package/src/lib/jobs/ai-job-email-step.js +547 -0
- package/src/lib/jobs/ai-job-email-step.js.map +1 -0
- package/src/lib/jobs/ai-job-experiment-step.d.ts +105 -0
- package/src/lib/jobs/ai-job-experiment-step.js +445 -0
- package/src/lib/jobs/ai-job-experiment-step.js.map +1 -0
- package/src/lib/jobs/ai-job-form-step.d.ts +250 -0
- package/src/lib/jobs/ai-job-form-step.js +634 -0
- package/src/lib/jobs/ai-job-form-step.js.map +1 -0
- package/src/lib/jobs/ai-job-generation.d.ts +114 -0
- package/src/lib/jobs/ai-job-generation.js +384 -0
- package/src/lib/jobs/ai-job-generation.js.map +1 -0
- package/src/lib/jobs/ai-job-insight-budget.d.ts +41 -0
- package/src/lib/jobs/ai-job-insight-budget.js +46 -0
- package/src/lib/jobs/ai-job-insight-budget.js.map +1 -0
- package/src/lib/jobs/ai-job-insight-step.d.ts +58 -0
- package/src/lib/jobs/ai-job-insight-step.js +350 -0
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -0
- package/src/lib/jobs/ai-job-layout-step.d.ts +103 -0
- package/src/lib/jobs/ai-job-layout-step.js +340 -0
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -0
- package/src/lib/jobs/ai-job-page-budget.d.ts +57 -0
- package/src/lib/jobs/ai-job-page-budget.js +60 -0
- package/src/lib/jobs/ai-job-page-budget.js.map +1 -0
- package/src/lib/jobs/ai-job-page-sections.d.ts +150 -0
- package/src/lib/jobs/ai-job-page-sections.js +450 -0
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -0
- package/src/lib/jobs/ai-job-page-step.d.ts +198 -0
- package/src/lib/jobs/ai-job-page-step.js +667 -0
- package/src/lib/jobs/ai-job-page-step.js.map +1 -0
- package/src/lib/jobs/ai-job-plan-conformance.d.ts +107 -0
- package/src/lib/jobs/ai-job-plan-conformance.js +164 -0
- package/src/lib/jobs/ai-job-plan-conformance.js.map +1 -0
- package/src/lib/jobs/ai-job-plan-step.d.ts +196 -0
- package/src/lib/jobs/ai-job-plan-step.js +458 -0
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -0
- package/src/lib/jobs/ai-job-plugin-drafts.d.ts +76 -0
- package/src/lib/jobs/ai-job-plugin-drafts.js +91 -0
- package/src/lib/jobs/ai-job-plugin-drafts.js.map +1 -0
- package/src/lib/jobs/ai-job-products-step.d.ts +106 -0
- package/src/lib/jobs/ai-job-products-step.js +436 -0
- package/src/lib/jobs/ai-job-products-step.js.map +1 -0
- package/src/lib/jobs/ai-job-seo-budget.d.ts +52 -0
- package/src/lib/jobs/ai-job-seo-budget.js +63 -0
- package/src/lib/jobs/ai-job-seo-budget.js.map +1 -0
- package/src/lib/jobs/ai-job-seo-step.d.ts +145 -0
- package/src/lib/jobs/ai-job-seo-step.js +809 -0
- package/src/lib/jobs/ai-job-seo-step.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +252 -0
- package/src/lib/jobs/ai-job-site-step.js +538 -0
- package/src/lib/jobs/ai-job-site-step.js.map +1 -0
- package/src/lib/jobs/ai-job-template-step.d.ts +105 -0
- package/src/lib/jobs/ai-job-template-step.js +391 -0
- package/src/lib/jobs/ai-job-template-step.js.map +1 -0
- package/src/lib/jobs/ai-job-text-step.d.ts +125 -0
- package/src/lib/jobs/ai-job-text-step.js +128 -0
- package/src/lib/jobs/ai-job-text-step.js.map +1 -0
- package/src/lib/jobs/ai-job-theme-budget.d.ts +38 -0
- package/src/lib/jobs/ai-job-theme-budget.js +43 -0
- package/src/lib/jobs/ai-job-theme-budget.js.map +1 -0
- package/src/lib/jobs/ai-job-theme-step.d.ts +115 -0
- package/src/lib/jobs/ai-job-theme-step.js +337 -0
- package/src/lib/jobs/ai-job-theme-step.js.map +1 -0
- package/src/lib/jobs/ai-job-workflow-step.d.ts +139 -0
- package/src/lib/jobs/ai-job-workflow-step.js +530 -0
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -0
- package/src/lib/jobs/ai-jobs-beat.d.ts +78 -0
- package/src/lib/jobs/ai-jobs-beat.js +93 -0
- package/src/lib/jobs/ai-jobs-beat.js.map +1 -0
- package/src/lib/jobs/ai-jobs-pause.d.ts +38 -0
- package/src/lib/jobs/ai-jobs-pause.js +45 -0
- package/src/lib/jobs/ai-jobs-pause.js.map +1 -0
- package/src/lib/jobs/ai-jobs.d.ts +558 -0
- package/src/lib/jobs/ai-jobs.js +1410 -0
- package/src/lib/jobs/ai-jobs.js.map +1 -0
- package/src/lib/jobs/ai-media-asset-server.d.ts +34 -0
- package/src/lib/jobs/ai-media-asset-server.js +34 -0
- package/src/lib/jobs/ai-media-asset-server.js.map +1 -0
- package/src/lib/jobs/ai-media-asset.d.ts +59 -0
- package/src/lib/jobs/ai-media-asset.js +86 -0
- package/src/lib/jobs/ai-media-asset.js.map +1 -0
- package/src/lib/jobs/ai-product-image.d.ts +78 -0
- package/src/lib/jobs/ai-product-image.js +113 -0
- package/src/lib/jobs/ai-product-image.js.map +1 -0
- package/src/lib/jobs/ai-theme-brand-inputs.d.ts +125 -0
- package/src/lib/jobs/ai-theme-brand-inputs.js +378 -0
- package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -0
- package/src/lib/jobs/ai-theme-brand-server.d.ts +33 -0
- package/src/lib/jobs/ai-theme-brand-server.js +33 -0
- package/src/lib/jobs/ai-theme-brand-server.js.map +1 -0
- package/src/lib/jobs/ai-workflow-records.d.ts +84 -0
- package/src/lib/jobs/ai-workflow-records.js +158 -0
- package/src/lib/jobs/ai-workflow-records.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-free-page-built-recording.d.ts +31 -0
- package/src/lib/jobs/fixtures/ai-free-page-built-recording.js +367 -0
- package/src/lib/jobs/fixtures/ai-free-page-built-recording.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-free-page-recording.d.ts +69 -0
- package/src/lib/jobs/fixtures/ai-free-page-recording.js +442 -0
- package/src/lib/jobs/fixtures/ai-free-page-recording.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-page-briefs.d.ts +146 -0
- package/src/lib/jobs/fixtures/ai-page-briefs.js +1850 -0
- package/src/lib/jobs/fixtures/ai-page-briefs.js.map +1 -0
- package/src/lib/model/ai-allotments.d.ts +208 -0
- package/src/lib/model/ai-allotments.js +269 -0
- package/src/lib/model/ai-allotments.js.map +1 -0
- package/src/lib/model/ai-automation-draft.d.ts +74 -0
- package/src/lib/model/ai-automation-draft.js +310 -0
- package/src/lib/model/ai-automation-draft.js.map +1 -0
- package/src/lib/model/ai-automation-outline.d.ts +50 -0
- package/src/lib/model/ai-automation-outline.js +262 -0
- package/src/lib/model/ai-automation-outline.js.map +1 -0
- package/src/lib/model/ai-build-plan.d.ts +197 -0
- package/src/lib/model/ai-build-plan.js +438 -0
- package/src/lib/model/ai-build-plan.js.map +1 -0
- package/src/lib/model/ai-crm.d.ts +237 -0
- package/src/lib/model/ai-crm.js +225 -0
- package/src/lib/model/ai-crm.js.map +1 -0
- package/src/lib/model/ai-experiment-proposal.d.ts +68 -0
- package/src/lib/model/ai-experiment-proposal.js +146 -0
- package/src/lib/model/ai-experiment-proposal.js.map +1 -0
- package/src/lib/model/ai-experiment.d.ts +309 -0
- package/src/lib/model/ai-experiment.js +395 -0
- package/src/lib/model/ai-experiment.js.map +1 -0
- package/src/lib/model/ai-insight.d.ts +178 -0
- package/src/lib/model/ai-insight.js +201 -0
- package/src/lib/model/ai-insight.js.map +1 -0
- package/src/lib/model/ai-jobs.types.d.ts +412 -0
- package/src/lib/model/ai-jobs.types.js +42 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -0
- package/src/lib/model/ai-page-job.d.ts +92 -0
- package/src/lib/model/ai-page-job.js +159 -0
- package/src/lib/model/ai-page-job.js.map +1 -0
- package/src/lib/model/ai-permissions.d.ts +49 -0
- package/src/lib/model/ai-permissions.js +97 -0
- package/src/lib/model/ai-permissions.js.map +1 -0
- package/src/lib/model/ai-plan-capabilities.d.ts +139 -0
- package/src/lib/model/ai-plan-capabilities.js +149 -0
- package/src/lib/model/ai-plan-capabilities.js.map +1 -0
- package/src/lib/model/ai-products.d.ts +251 -0
- package/src/lib/model/ai-products.js +253 -0
- package/src/lib/model/ai-products.js.map +1 -0
- package/src/lib/model/ai-seo.d.ts +208 -0
- package/src/lib/model/ai-seo.js +150 -0
- package/src/lib/model/ai-seo.js.map +1 -0
- package/src/lib/model/ai-site-inventory.d.ts +164 -0
- package/src/lib/model/ai-site-inventory.js +145 -0
- package/src/lib/model/ai-site-inventory.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +247 -0
- package/src/lib/model/ai-site-job.js +314 -0
- package/src/lib/model/ai-site-job.js.map +1 -0
- package/src/lib/model/ai-site-start-seo.d.ts +103 -0
- package/src/lib/model/ai-site-start-seo.js +154 -0
- package/src/lib/model/ai-site-start-seo.js.map +1 -0
- package/src/lib/model/ai-site-start.d.ts +122 -0
- package/src/lib/model/ai-site-start.js +159 -0
- package/src/lib/model/ai-site-start.js.map +1 -0
- package/src/lib/model/ai-site-switch.d.ts +56 -0
- package/src/lib/model/ai-site-switch.js +73 -0
- package/src/lib/model/ai-site-switch.js.map +1 -0
- package/src/lib/model/ai-storefront-claims.d.ts +70 -0
- package/src/lib/model/ai-storefront-claims.js +130 -0
- package/src/lib/model/ai-storefront-claims.js.map +1 -0
- package/src/lib/model/ai-template-subjects.d.ts +82 -0
- package/src/lib/model/ai-template-subjects.js +136 -0
- package/src/lib/model/ai-template-subjects.js.map +1 -0
- package/src/lib/model/ai-theme-proposal.d.ts +210 -0
- package/src/lib/model/ai-theme-proposal.js +506 -0
- package/src/lib/model/ai-theme-proposal.js.map +1 -0
- package/src/lib/model/ai-tokens.d.ts +96 -0
- package/src/lib/model/ai-tokens.js +117 -0
- package/src/lib/model/ai-tokens.js.map +1 -0
- package/src/lib/model/ai-usage-by-user.d.ts +120 -0
- package/src/lib/model/ai-usage-by-user.js +139 -0
- package/src/lib/model/ai-usage-by-user.js.map +1 -0
- package/src/lib/model/ai-workflow-job.d.ts +111 -0
- package/src/lib/model/ai-workflow-job.js +183 -0
- package/src/lib/model/ai-workflow-job.js.map +1 -0
- package/src/lib/model/assist-edit.d.ts +261 -0
- package/src/lib/model/assist-edit.js +175 -0
- package/src/lib/model/assist-edit.js.map +1 -0
- package/src/lib/plugin-config.d.ts +33 -0
- package/src/lib/plugin-config.js +118 -0
- package/src/lib/plugin-config.js.map +1 -0
- package/src/lib/plugin.d.ts +30 -0
- package/src/lib/plugin.js +525 -0
- package/src/lib/plugin.js.map +1 -0
- package/src/lib/providers/anthropic.d.ts +84 -0
- package/src/lib/providers/anthropic.js +432 -0
- package/src/lib/providers/anthropic.js.map +1 -0
- package/src/lib/providers/catalog.d.ts +197 -0
- package/src/lib/providers/catalog.js +312 -0
- package/src/lib/providers/catalog.js.map +1 -0
- package/src/lib/providers/contract.d.ts +464 -0
- package/src/lib/providers/contract.js +274 -0
- package/src/lib/providers/contract.js.map +1 -0
- package/src/lib/providers/model-choice.d.ts +119 -0
- package/src/lib/providers/model-choice.js +416 -0
- package/src/lib/providers/model-choice.js.map +1 -0
- package/src/lib/providers/openai-compatible.d.ts +58 -0
- package/src/lib/providers/openai-compatible.js +361 -0
- package/src/lib/providers/openai-compatible.js.map +1 -0
- package/src/lib/providers/registry.d.ts +49 -0
- package/src/lib/providers/registry.js +71 -0
- package/src/lib/providers/registry.js.map +1 -0
- package/src/lib/providers/routing.d.ts +98 -0
- package/src/lib/providers/routing.js +450 -0
- package/src/lib/providers/routing.js.map +1 -0
- package/src/lib/runtime/ai-abuse-guards.d.ts +113 -0
- package/src/lib/runtime/ai-abuse-guards.js +148 -0
- package/src/lib/runtime/ai-abuse-guards.js.map +1 -0
- package/src/lib/runtime/ai-component-bindings.d.ts +67 -0
- package/src/lib/runtime/ai-component-bindings.js +106 -0
- package/src/lib/runtime/ai-component-bindings.js.map +1 -0
- package/src/lib/runtime/ai-device-audit.d.ts +109 -0
- package/src/lib/runtime/ai-device-audit.js +97 -0
- package/src/lib/runtime/ai-device-audit.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +720 -0
- package/src/lib/runtime/ai-doctrine-validators.js +3133 -0
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -0
- package/src/lib/runtime/ai-doctrine.d.ts +372 -0
- package/src/lib/runtime/ai-doctrine.js +944 -0
- package/src/lib/runtime/ai-doctrine.js.map +1 -0
- package/src/lib/runtime/ai-eval-live.d.ts +321 -0
- package/src/lib/runtime/ai-eval-live.js +1257 -0
- package/src/lib/runtime/ai-eval-live.js.map +1 -0
- package/src/lib/runtime/ai-eval-memory-firestore.d.ts +51 -0
- package/src/lib/runtime/ai-eval-memory-firestore.js +183 -0
- package/src/lib/runtime/ai-eval-memory-firestore.js.map +1 -0
- package/src/lib/runtime/ai-eval.d.ts +441 -0
- package/src/lib/runtime/ai-eval.js +1151 -0
- package/src/lib/runtime/ai-eval.js.map +1 -0
- package/src/lib/runtime/ai-gate.d.ts +130 -0
- package/src/lib/runtime/ai-gate.js +272 -0
- package/src/lib/runtime/ai-gate.js.map +1 -0
- package/src/lib/runtime/ai-generation-bounds.d.ts +24 -0
- package/src/lib/runtime/ai-generation-bounds.js +23 -0
- package/src/lib/runtime/ai-generation-bounds.js.map +1 -0
- package/src/lib/runtime/ai-insight-check.d.ts +76 -0
- package/src/lib/runtime/ai-insight-check.js +281 -0
- package/src/lib/runtime/ai-insight-check.js.map +1 -0
- package/src/lib/runtime/ai-node-tree.d.ts +181 -0
- package/src/lib/runtime/ai-node-tree.js +931 -0
- package/src/lib/runtime/ai-node-tree.js.map +1 -0
- package/src/lib/runtime/ai-page-links.d.ts +79 -0
- package/src/lib/runtime/ai-page-links.js +135 -0
- package/src/lib/runtime/ai-page-links.js.map +1 -0
- package/src/lib/runtime/ai-palette.d.ts +252 -0
- package/src/lib/runtime/ai-palette.generated.d.ts +34 -0
- package/src/lib/runtime/ai-palette.generated.js +5263 -0
- package/src/lib/runtime/ai-palette.generated.js.map +1 -0
- package/src/lib/runtime/ai-palette.js +187 -0
- package/src/lib/runtime/ai-palette.js.map +1 -0
- package/src/lib/runtime/ai-products-generation.d.ts +107 -0
- package/src/lib/runtime/ai-products-generation.js +250 -0
- package/src/lib/runtime/ai-products-generation.js.map +1 -0
- package/src/lib/runtime/ai-repeated-items.d.ts +95 -0
- package/src/lib/runtime/ai-repeated-items.js +397 -0
- package/src/lib/runtime/ai-repeated-items.js.map +1 -0
- package/src/lib/runtime/ai-runtime.d.ts +148 -0
- package/src/lib/runtime/ai-runtime.js +237 -0
- package/src/lib/runtime/ai-runtime.js.map +1 -0
- package/src/lib/runtime/ai-template-examples.d.ts +71 -0
- package/src/lib/runtime/ai-template-examples.js +135 -0
- package/src/lib/runtime/ai-template-examples.js.map +1 -0
- package/src/lib/runtime/assist-answer-cache.d.ts +80 -0
- package/src/lib/runtime/assist-answer-cache.js +184 -0
- package/src/lib/runtime/assist-answer-cache.js.map +1 -0
- package/src/lib/runtime/seo-audit.d.ts +93 -0
- package/src/lib/runtime/seo-audit.js +260 -0
- package/src/lib/runtime/seo-audit.js.map +1 -0
- package/src/lib/runtime/seo-content-fixes.d.ts +41 -0
- package/src/lib/runtime/seo-content-fixes.js +168 -0
- package/src/lib/runtime/seo-content-fixes.js.map +1 -0
- package/src/lib/runtime/seo-fields.d.ts +152 -0
- package/src/lib/runtime/seo-fields.js +215 -0
- package/src/lib/runtime/seo-fields.js.map +1 -0
- package/src/lib/runtime/seo-page-facts.d.ts +61 -0
- package/src/lib/runtime/seo-page-facts.js +150 -0
- package/src/lib/runtime/seo-page-facts.js.map +1 -0
- package/src/lib/runtime/site-inventory.d.ts +63 -0
- package/src/lib/runtime/site-inventory.js +230 -0
- package/src/lib/runtime/site-inventory.js.map +1 -0
- package/src/lib/server/ai-admin-org.d.ts +33 -0
- package/src/lib/server/ai-admin-org.js +381 -0
- package/src/lib/server/ai-admin-org.js.map +1 -0
- package/src/lib/server/ai-admin-orgs-spend.d.ts +40 -0
- package/src/lib/server/ai-admin-orgs-spend.js +129 -0
- package/src/lib/server/ai-admin-orgs-spend.js.map +1 -0
- package/src/lib/server/ai-admin-overage.d.ts +19 -0
- package/src/lib/server/ai-admin-overage.js +246 -0
- package/src/lib/server/ai-admin-overage.js.map +1 -0
- package/src/lib/server/ai-admin-signals.d.ts +34 -0
- package/src/lib/server/ai-admin-signals.js +330 -0
- package/src/lib/server/ai-admin-signals.js.map +1 -0
- package/src/lib/server/ai-admin-user.d.ts +27 -0
- package/src/lib/server/ai-admin-user.js +128 -0
- package/src/lib/server/ai-admin-user.js.map +1 -0
- package/src/lib/server/ai-allotments.d.ts +27 -0
- package/src/lib/server/ai-allotments.js +544 -0
- package/src/lib/server/ai-allotments.js.map +1 -0
- package/src/lib/server/ai-assist-prompts.d.ts +102 -0
- package/src/lib/server/ai-assist-prompts.js +207 -0
- package/src/lib/server/ai-assist-prompts.js.map +1 -0
- package/src/lib/server/ai-assist.d.ts +119 -0
- package/src/lib/server/ai-assist.js +517 -0
- package/src/lib/server/ai-assist.js.map +1 -0
- package/src/lib/server/ai-crm-answer.d.ts +30 -0
- package/src/lib/server/ai-crm-answer.js +98 -0
- package/src/lib/server/ai-crm-answer.js.map +1 -0
- package/src/lib/server/ai-generate-component.d.ts +111 -0
- package/src/lib/server/ai-generate-component.js +643 -0
- package/src/lib/server/ai-generate-component.js.map +1 -0
- package/src/lib/server/ai-host-permissions.d.ts +46 -0
- package/src/lib/server/ai-host-permissions.js +204 -0
- package/src/lib/server/ai-host-permissions.js.map +1 -0
- package/src/lib/server/ai-insight-answer.d.ts +30 -0
- package/src/lib/server/ai-insight-answer.js +83 -0
- package/src/lib/server/ai-insight-answer.js.map +1 -0
- package/src/lib/server/ai-insight-digest-route.d.ts +30 -0
- package/src/lib/server/ai-insight-digest-route.js +156 -0
- package/src/lib/server/ai-insight-digest-route.js.map +1 -0
- package/src/lib/server/ai-jobs-batch.d.ts +48 -0
- package/src/lib/server/ai-jobs-batch.js +306 -0
- package/src/lib/server/ai-jobs-batch.js.map +1 -0
- package/src/lib/server/ai-jobs-beat-route.d.ts +36 -0
- package/src/lib/server/ai-jobs-beat-route.js +86 -0
- package/src/lib/server/ai-jobs-beat-route.js.map +1 -0
- package/src/lib/server/ai-jobs-cancel.d.ts +30 -0
- package/src/lib/server/ai-jobs-cancel.js +74 -0
- package/src/lib/server/ai-jobs-cancel.js.map +1 -0
- package/src/lib/server/ai-jobs-events-route.d.ts +31 -0
- package/src/lib/server/ai-jobs-events-route.js +51 -0
- package/src/lib/server/ai-jobs-events-route.js.map +1 -0
- package/src/lib/server/ai-jobs-events.d.ts +55 -0
- package/src/lib/server/ai-jobs-events.js +144 -0
- package/src/lib/server/ai-jobs-events.js.map +1 -0
- package/src/lib/server/ai-jobs-gate.d.ts +47 -0
- package/src/lib/server/ai-jobs-gate.js +106 -0
- package/src/lib/server/ai-jobs-gate.js.map +1 -0
- package/src/lib/server/ai-jobs-resume.d.ts +23 -0
- package/src/lib/server/ai-jobs-resume.js +209 -0
- package/src/lib/server/ai-jobs-resume.js.map +1 -0
- package/src/lib/server/ai-jobs-route.d.ts +32 -0
- package/src/lib/server/ai-jobs-route.js +307 -0
- package/src/lib/server/ai-jobs-route.js.map +1 -0
- package/src/lib/server/ai-models.d.ts +18 -0
- package/src/lib/server/ai-models.js +134 -0
- package/src/lib/server/ai-models.js.map +1 -0
- package/src/lib/server/ai-seo-apply.d.ts +53 -0
- package/src/lib/server/ai-seo-apply.js +294 -0
- package/src/lib/server/ai-seo-apply.js.map +1 -0
- package/src/lib/server/ai-usage.d.ts +18 -0
- package/src/lib/server/ai-usage.js +220 -0
- package/src/lib/server/ai-usage.js.map +1 -0
- package/src/lib/server/assist-chat.d.ts +234 -0
- package/src/lib/server/assist-chat.js +1446 -0
- package/src/lib/server/assist-chat.js.map +1 -0
- package/src/lib/server/assist-edit-applied.d.ts +22 -0
- package/src/lib/server/assist-edit-applied.js +239 -0
- package/src/lib/server/assist-edit-applied.js.map +1 -0
- package/src/lib/server/assist-edit.d.ts +124 -0
- package/src/lib/server/assist-edit.js +1000 -0
- package/src/lib/server/assist-edit.js.map +1 -0
- package/src/lib/server/assist-feedback.d.ts +31 -0
- package/src/lib/server/assist-feedback.js +115 -0
- package/src/lib/server/assist-feedback.js.map +1 -0
- package/src/lib/server/assist-view-context.d.ts +343 -0
- package/src/lib/server/assist-view-context.js +690 -0
- package/src/lib/server/assist-view-context.js.map +1 -0
- package/src/lib/server/billing-credits.d.ts +59 -0
- package/src/lib/server/billing-credits.js +146 -0
- package/src/lib/server/billing-credits.js.map +1 -0
- package/src/lib/server/billing-overage.d.ts +19 -0
- package/src/lib/server/billing-overage.js +337 -0
- package/src/lib/server/billing-overage.js.map +1 -0
- package/src/lib/server.d.ts +32 -0
- package/src/lib/server.js +265 -0
- package/src/lib/server.js.map +1 -0
- package/src/lib/subprocessors.d.ts +18 -0
- package/src/lib/subprocessors.js +52 -0
- package/src/lib/subprocessors.js.map +1 -0
- package/src/lib/tools/ai-component-tool.d.ts +99 -0
- package/src/lib/tools/ai-component-tool.js +369 -0
- package/src/lib/tools/ai-component-tool.js.map +1 -0
- package/src/lib/tools/ai-crm-tool.d.ts +106 -0
- package/src/lib/tools/ai-crm-tool.js +488 -0
- package/src/lib/tools/ai-crm-tool.js.map +1 -0
- package/src/lib/tools/ai-duplicate-resource-tool.d.ts +71 -0
- package/src/lib/tools/ai-duplicate-resource-tool.js +164 -0
- package/src/lib/tools/ai-duplicate-resource-tool.js.map +1 -0
- package/src/lib/tools/ai-experiment-tool.d.ts +40 -0
- package/src/lib/tools/ai-experiment-tool.js +155 -0
- package/src/lib/tools/ai-experiment-tool.js.map +1 -0
- package/src/lib/tools/ai-insight-tool.d.ts +46 -0
- package/src/lib/tools/ai-insight-tool.js +200 -0
- package/src/lib/tools/ai-insight-tool.js.map +1 -0
- package/src/lib/tools/ai-inventory-lookup-tool.d.ts +86 -0
- package/src/lib/tools/ai-inventory-lookup-tool.js +164 -0
- package/src/lib/tools/ai-inventory-lookup-tool.js.map +1 -0
- package/src/lib/tools/ai-products-tool.d.ts +65 -0
- package/src/lib/tools/ai-products-tool.js +644 -0
- package/src/lib/tools/ai-products-tool.js.map +1 -0
- package/src/lib/tools/ai-seo-tool.d.ts +162 -0
- package/src/lib/tools/ai-seo-tool.js +546 -0
- package/src/lib/tools/ai-seo-tool.js.map +1 -0
- package/src/lib/tools/ai-theme-tool.d.ts +101 -0
- package/src/lib/tools/ai-theme-tool.js +562 -0
- package/src/lib/tools/ai-theme-tool.js.map +1 -0
- package/src/lib/tools/ai-workflow-tool.d.ts +166 -0
- package/src/lib/tools/ai-workflow-tool.js +860 -0
- package/src/lib/tools/ai-workflow-tool.js.map +1 -0
- package/src/lib/usage/ai-allotment-alerts.d.ts +72 -0
- package/src/lib/usage/ai-allotment-alerts.js +157 -0
- package/src/lib/usage/ai-allotment-alerts.js.map +1 -0
- package/src/lib/usage/ai-allotments.d.ts +125 -0
- package/src/lib/usage/ai-allotments.js +239 -0
- package/src/lib/usage/ai-allotments.js.map +1 -0
- package/src/lib/usage/ai-model-sample.d.ts +36 -0
- package/src/lib/usage/ai-model-sample.js +68 -0
- package/src/lib/usage/ai-model-sample.js.map +1 -0
- package/src/lib/usage/ai-usage-alerts.d.ts +55 -0
- package/src/lib/usage/ai-usage-alerts.js +114 -0
- package/src/lib/usage/ai-usage-alerts.js.map +1 -0
- package/src/lib/usage/ai-usage-by-user.d.ts +161 -0
- package/src/lib/usage/ai-usage-by-user.js +211 -0
- package/src/lib/usage/ai-usage-by-user.js.map +1 -0
- package/src/lib/usage/ai-usage-eraser.d.ts +29 -0
- package/src/lib/usage/ai-usage-eraser.js +40 -0
- package/src/lib/usage/ai-usage-eraser.js.map +1 -0
- package/src/lib/usage/ai-usage-meter.d.ts +39 -0
- package/src/lib/usage/ai-usage-meter.js +85 -0
- package/src/lib/usage/ai-usage-meter.js.map +1 -0
- package/src/lib/usage/ai-usage-wire.d.ts +184 -0
- package/src/lib/usage/ai-usage-wire.js +98 -0
- package/src/lib/usage/ai-usage-wire.js.map +1 -0
- package/src/lib/usage/assist-ceiling.d.ts +87 -0
- package/src/lib/usage/assist-ceiling.js +110 -0
- package/src/lib/usage/assist-ceiling.js.map +1 -0
- package/src/lib/usage/assist-free-taste.d.ts +225 -0
- package/src/lib/usage/assist-free-taste.js +308 -0
- package/src/lib/usage/assist-free-taste.js.map +1 -0
- package/src/lib/usage/assist-refusals.d.ts +69 -0
- package/src/lib/usage/assist-refusals.js +79 -0
- package/src/lib/usage/assist-refusals.js.map +1 -0
- package/src/lib/usage/assist-signal-mining.d.ts +375 -0
- package/src/lib/usage/assist-signal-mining.js +344 -0
- package/src/lib/usage/assist-signal-mining.js.map +1 -0
- package/src/lib/usage/assist-usage.d.ts +571 -0
- package/src/lib/usage/assist-usage.js +978 -0
- package/src/lib/usage/assist-usage.js.map +1 -0
- package/src/lib/usage/staff-org-ai.d.ts +242 -0
- package/src/lib/usage/staff-org-ai.js +173 -0
- package/src/lib/usage/staff-org-ai.js.map +1 -0
- package/src/lib/usage/staff-org-usage-ai.d.ts +83 -0
- package/src/lib/usage/staff-org-usage-ai.js +59 -0
- package/src/lib/usage/staff-org-usage-ai.js.map +1 -0
- package/src/lib/usage/staff-orgs-ai-spend.d.ts +83 -0
- package/src/lib/usage/staff-orgs-ai-spend.js +114 -0
- package/src/lib/usage/staff-orgs-ai-spend.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/server/ai-generate-component.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// lockdown-423: via libs/plugins/ai/src/lib/runtime/ai-gate.ts\n// The POST climbs `aiGateLadder`, whose lockdown rung is the verdict.\n\nimport { NODE_HIDE_IF_PROP } from '@aglyn/aglyn/app-utils/compose-reusable-components'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport {\n ASSIST_EDIT_ACTION_ID,\n assistEditDocumentOf,\n summarizeAssistEditOps,\n type AssistEditCanvasContext,\n type AssistEditCanvasNode,\n type AssistEditComponentProp,\n type AssistEditPropBinding,\n type AssistEditProposal,\n type AssistEditSaveAsComponentOp,\n type AssistEditTarget,\n} from '../model/assist-edit'\nimport type { AiSystemBlock, AiTool } from '../providers/contract'\nimport { AI_ROUTING_TABLE } from '../providers/routing'\nimport {\n aiComponentKindBindsTo,\n aiComponentPropBindsToField,\n aiComponentUnofferedAnswers,\n type AiComponentPropBinding,\n} from '../runtime/ai-component-bindings'\nimport {\n runValidatedGeneration,\n type AiCustomGenerationInput,\n type AiGenerationCheckResult,\n type AiValidatedGeneration,\n} from '../runtime/ai-doctrine'\nimport type { AiDoctrineViolation } from '../runtime/ai-doctrine-validators'\nimport { aiGateLadder } from '../runtime/ai-gate'\nimport { AI_PALETTE } from '../runtime/ai-palette.generated'\nimport {\n AI_COMPONENT_MAX_PROPS,\n AI_COMPONENT_SELECTION_PROP_KINDS,\n aiComponentPropKindWords,\n} from '../tools/ai-component-tool'\nimport { recordAssistExchange, releaseAssistMessage } from '../usage/assist-usage'\nimport { parseAssistEditContext } from './assist-edit'\n\n/**\n * Save the selection as a reusable component, with AI (AGL-2908):\n * `POST /api/ai/generate/component { orgId, hostId, route, canvas, name }`.\n *\n * The second entry point of the component job. The first builds a component\n * from a brief; this one reads a section the person already has and proposes\n * which of its values become properties — the judgment a person makes in\n * FILE ▸ Properties…, made from the same evidence.\n *\n * ## What it sends, and nothing else\n *\n * The outline of the open page, component or layout — its elements, their\n * names, their shortened settings and text, and the selected element's own\n * styles — and the name the member typed for the component. It reads no\n * other page, no entry, no product, no contact and no site inventory, so\n * none of them is sent. The `AI_DOORS` entry says so.\n *\n * ## It answers references, never a document\n *\n * `props[]` without defaults, and `bindings[]` of `{ nodeId, field, prop }`.\n * The outline shortens every string, so the server has no full value to make\n * a default out of and never invents one: the client reads each bound\n * field's current value off the live canvas as it writes the token in, which\n * is what makes the instance render exactly what was there.\n *\n * ## It writes nothing\n *\n * The answer is an `AssistEditProposal` carrying one `saveAsComponent` op,\n * shown on AGL-2906's own card. The member applies it, and the apply creates\n * the component through the host resources route and swaps the subtree for\n * an instance on the open draft. This door touches no document at all.\n *\n * ## The rungs\n *\n * `aiGateLadder`: the `aiGenerative` entitlement, `release_ai_generative`,\n * the `ai-generate` lockdown switch, `ai.generate` on the site the body\n * named, a per-uid window and a reservation. A refusal before the model runs\n * hands the reservation back.\n */\n\n/** The name of the structured-output tool the proposal arrives through. */\nexport const AI_COMPONENT_SELECTION_TOOL_NAME = 'propose_component_from_selection'\n\n/** The doctrine kind this step runs under; it has no tree of its own to read. */\nexport const AI_COMPONENT_SELECTION_KIND = 'component-selection'\n\n/**\n * The answer ceiling. A proposal is a property list and a binding list —\n * references, never a tree — so it needs a fraction of what the from-brief\n * step's answer does.\n */\nexport const AI_COMPONENT_SELECTION_MAX_TOKENS = 4_000\n\nconst RATE_LIMIT = { key: 'ai-generate-component', limit: 10, windowMs: 60_000 }\n\n/** The longest name a component is created under, as the promote dialog takes one. */\nconst NAME_CHARS = 80\n/** The longest summary the card shows. */\nconst SUMMARY_CHARS = 160\n\nconst NAME_MAX = 40\nconst LABEL_MAX = 80\nconst HELP_MAX = 200\n\nconst PROP_NAME = /^[A-Za-z][A-Za-z0-9_]{0,39}$/\nconst FIELD_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,40}$/\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/** Every control character and line separator as a space, then squeezed and cut. */\nfunction cleanText(value: unknown, limit: number): string {\n let out = ''\n for (const char of String(value ?? '')) {\n const code = char.charCodeAt(0)\n out +=\n code < 32 || code === 127 || code === 0x85 || code === 0x2028 || code === 0x2029\n ? ' '\n : char\n }\n return out.replace(/\\s+/g, ' ').trim().slice(0, limit)\n}\n\n/* ------------------------------------------------------------------ *\n * The selection\n * ------------------------------------------------------------------ */\n\n/** The element ids of the selection's subtree, the selection included. */\nexport function aiComponentSelectionIds(\n context: AssistEditCanvasContext,\n selectedId: string,\n): Set<string> {\n const byParent = new Map<string, string[]>()\n for (const node of context.nodes) {\n if (!node.parentId) continue\n byParent.set(node.parentId, [...(byParent.get(node.parentId) ?? []), node.id])\n }\n const found = new Set<string>([selectedId])\n const queue = [...(byParent.get(selectedId) ?? [])]\n while (queue.length) {\n const id = queue.shift() as string\n if (found.has(id)) continue\n found.add(id)\n queue.push(...(byParent.get(id) ?? []))\n }\n return found\n}\n\n/** A heading's level in the outline: the element it renders as, else its variant. */\nfunction headingLevel(node: AssistEditCanvasNode): number | null {\n if (node.componentId !== 'muiTypography') return null\n const props = node.props ?? {}\n const element = typeof props['component'] === 'string' ? props['component'] : ''\n const match = /^h([1-6])$/.exec(element || String(props['variant'] ?? ''))\n return match ? Number(match[1]) : null\n}\n\n/** Whichever element a node was told to render as. */\nconst elementOf = (node: AssistEditCanvasNode): string => {\n const value = node.props?.['component'] ?? node.props?.['element']\n return typeof value === 'string' ? value : ''\n}\n\n/** Why this selection cannot become a component; `null` when it can. */\nexport function aiComponentSelectionRefusal(\n context: AssistEditCanvasContext,\n selectedId: string | null,\n): string | null {\n if (!selectedId || selectedId === CANVAS_ROOT_ELEMENT_ID) {\n return 'Select the section you want to save as a component first.'\n }\n const described = new Map(context.nodes.map((node) => [node.id, node]))\n const selection = described.get(selectedId)\n if (!selection) {\n return 'Select the section you want to save as a component first.'\n }\n const ids = aiComponentSelectionIds(context, selectedId)\n // The outline stops at a cap, so a selection larger than it can describe is\n // one the model would read half of. Refuse rather than guess at the rest.\n for (const id of ids) {\n const node = described.get(id)\n if (!node) return 'This section is too large to read all of. Pick a smaller part of it.'\n if (node.childCount > (context.nodes.filter((other) => other.parentId === id).length ?? 0)) {\n return 'This section is too large to read all of. Pick a smaller part of it.'\n }\n }\n // Rule 11, before any spend: a component is placed inside pages, so it\n // carries neither the main landmark nor a second top-level heading.\n const inside = [...ids].map((id) => described.get(id)).filter(Boolean) as AssistEditCanvasNode[]\n if (inside.some((node) => elementOf(node) === 'main')) {\n return 'This section is the page’s main landmark, which a component cannot carry. Pick the part inside it.'\n }\n if (inside.filter((node) => headingLevel(node) === 1).length > 1) {\n return 'This section has more than one top-level heading. A component carries at most one.'\n }\n return null\n}\n\n/* ------------------------------------------------------------------ *\n * The tool\n * ------------------------------------------------------------------ */\n\nconst STRING = { type: 'string' } as const\n\n/**\n * The strict tool the proposal arrives through. Every field is required and\n * flat — optional properties are not portable across strict tool\n * implementations — so a kind with no answers sends `[]`.\n */\nexport function aiComponentSelectionTool(): AiTool {\n return {\n name: AI_COMPONENT_SELECTION_TOOL_NAME,\n description:\n 'Propose the properties this section should declare once it is a reusable component, and where each one binds.',\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['summary', 'props', 'bindings'],\n properties: {\n summary: {\n type: 'string',\n description: 'One line, for the person deciding: what each page will be able to change.',\n },\n props: {\n type: 'array',\n description: 'The properties, in the order a page sets them.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['name', 'type', 'label', 'description', 'options'],\n properties: {\n name: {\n type: 'string',\n description: 'Letters, digits and underscores, starting with a letter.',\n },\n type: {\n type: 'string',\n enum: [...AI_COMPONENT_SELECTION_PROP_KINDS],\n description: `The kind, as type (the name a page reads): ${aiComponentPropKindWords(AI_COMPONENT_SELECTION_PROP_KINDS)}.`,\n },\n label: {\n type: 'string',\n description:\n 'The words a page reads beside the field. A property that hides an optional part is labeled \"Hide …\".',\n },\n description: { type: 'string', description: 'Help beside the field, or \"\".' },\n options: {\n type: 'array',\n description: 'The answers of a choice; [] for every other kind.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['value', 'label'],\n properties: {\n value: {\n type: 'string',\n description: 'What the bound field receives: one of the values that field lists.',\n },\n label: { type: 'string', description: 'What a page picks.' },\n },\n },\n },\n },\n },\n },\n bindings: {\n type: 'array',\n description: 'Where each property goes. Every property is bound at least once.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['nodeId', 'field', 'prop'],\n properties: {\n nodeId: {\n type: 'string',\n description: 'An element id from the selected section, as the outline gave it.',\n },\n field: {\n type: 'string',\n description: `A setting of that element, or \"${NODE_HIDE_IF_PROP}\" to hide an optional part.`,\n },\n prop: { type: 'string', description: 'The name of the property it takes.' },\n },\n },\n },\n },\n },\n }\n}\n\n/* ------------------------------------------------------------------ *\n * The check\n * ------------------------------------------------------------------ */\n\n/** What the model proposed, once it has been read and held to the selection. */\nexport interface AiComponentSelectionProposal {\n summary: string\n props: AssistEditComponentProp[]\n bindings: AssistEditPropBinding[]\n}\n\n/** The label a property that hides an optional part opens with. */\nconst HIDE_LABEL = /^Hide\\b/\n\nconst paletteEntry = (componentId: string) => AI_PALETTE[componentId]\n\n/**\n * The proposal, held to the selection it was made from.\n *\n * A CLOSED WORLD, like the edit rung's: every element named is one the\n * outline described, inside the selection. A binding outside it would tie a\n * property of the new component to an element the component does not\n * contain, and the token would render as its own marker on the page.\n *\n * Every other rule is the from-brief step's own, read from the same module,\n * so the two paths cannot disagree about which property fits which field.\n */\nexport function checkAiComponentSelection(\n answer: Record<string, unknown>,\n context: AssistEditCanvasContext,\n selectedId: string,\n): AiGenerationCheckResult<AiComponentSelectionProposal> {\n const violations: AiDoctrineViolation[] = []\n const finding = (code: string, message: string, path: string): void => {\n violations.push({ rule: 1, code, message, paths: [path] })\n }\n const inside = aiComponentSelectionIds(context, selectedId)\n const described = new Map(context.nodes.map((node) => [node.id, node]))\n\n const rawProps = Array.isArray(answer['props']) ? answer['props'] : null\n const rawBindings = Array.isArray(answer['bindings']) ? answer['bindings'] : null\n if (!rawProps || !rawBindings) {\n finding(\n 'proposal-missing',\n 'The proposal came without its properties or its bindings. Send both lists, even when one is empty.',\n 'props',\n )\n return { value: null, violations }\n }\n if (rawProps.length > AI_COMPONENT_MAX_PROPS) {\n finding(\n 'props-too-many',\n `A component declares at most ${AI_COMPONENT_MAX_PROPS} properties. Keep the ones a page really changes.`,\n 'props',\n )\n return { value: null, violations }\n }\n\n const props: AssistEditComponentProp[] = []\n const byName = new Map<string, AssistEditComponentProp>()\n rawProps.forEach((raw, index) => {\n const path = `props[${index}]`\n if (!isRecord(raw)) return finding('prop-shape', 'A property is not an object.', path)\n const name = cleanText(raw['name'], NAME_MAX)\n if (!PROP_NAME.test(name)) {\n return finding(\n 'prop-name',\n 'A property name is letters, digits and underscores, starting with a letter.',\n path,\n )\n }\n if (byName.has(name)) {\n return finding('prop-duplicate', `Two properties are called \"${name}\".`, path)\n }\n const type = cleanText(raw['type'], NAME_MAX)\n if (!(AI_COMPONENT_SELECTION_PROP_KINDS as readonly string[]).includes(type)) {\n return finding('prop-kind', `\"${type}\" is not a kind a property takes.`, path)\n }\n const options = Array.isArray(raw['options'])\n ? raw['options']\n .filter(isRecord)\n .map((option) => ({\n value: cleanText(option['value'], LABEL_MAX),\n label: cleanText(option['label'], LABEL_MAX),\n }))\n .filter((option) => option.value)\n : []\n if (type === 'choice' && !options.length) {\n return finding('prop-answers', `The choice \"${name}\" lists no answers.`, path)\n }\n const label = cleanText(raw['label'], LABEL_MAX)\n const description = cleanText(raw['description'], HELP_MAX)\n const prop: AssistEditComponentProp = {\n name,\n type: type as AssistEditComponentProp['type'],\n ...(label ? { label } : {}),\n ...(description ? { description } : {}),\n ...(options.length ? { options } : {}),\n }\n props.push(prop)\n byName.set(name, prop)\n })\n if (violations.length) return { value: null, violations }\n\n const bindings: AssistEditPropBinding[] = []\n const bound = new Set<string>()\n rawBindings.forEach((raw, index) => {\n const path = `bindings[${index}]`\n if (!isRecord(raw)) return finding('binding-shape', 'A binding is not an object.', path)\n const nodeId = cleanText(raw['nodeId'], 64)\n const node = described.get(nodeId)\n if (!node || !inside.has(nodeId)) {\n return finding(\n 'binding-outside',\n 'A property is bound to an element outside the section being saved. Bind only elements the section contains.',\n path,\n )\n }\n const field = cleanText(raw['field'], 40)\n if (!FIELD_NAME.test(field)) {\n return finding('binding-field', `\"${field}\" is not a setting of an element.`, path)\n }\n if (field === NODE_HIDE_IF_PROP && nodeId === selectedId) {\n return finding(\n 'binding-hides-all',\n 'A property cannot hide the whole component. Hide the optional part inside it.',\n path,\n )\n }\n const propName = cleanText(raw['prop'], NAME_MAX)\n const prop = byName.get(propName)\n if (!prop) {\n return finding('binding-undeclared', `No property is called \"${propName}\".`, path)\n }\n const entry = paletteEntry(node.componentId)\n const binding = prop as AiComponentPropBinding\n if (!aiComponentPropBindsToField(binding, entry, field, 'whole')) {\n return finding(\n 'binding-fit',\n `\"${prop.name}\" does not fit the \"${field}\" setting of that element. Bind it to a setting of its own kind.`,\n path,\n )\n }\n const unoffered = aiComponentUnofferedAnswers(binding, entry, field)\n if (unoffered.length) {\n return finding(\n 'binding-answers',\n `The answers ${unoffered.join(', ')} of \"${prop.name}\" are not ones that setting lists.`,\n path,\n )\n }\n if (field === NODE_HIDE_IF_PROP && !HIDE_LABEL.test(prop.label ?? '')) {\n return finding(\n 'hide-label',\n `A property that hides a part is labeled \"Hide …\", so a page reads what it does. \"${prop.name}\" is not.`,\n path,\n )\n }\n bound.add(propName)\n bindings.push({ nodeId, componentId: node.componentId, field, prop: propName })\n })\n if (violations.length) return { value: null, violations }\n\n for (const prop of props) {\n if (bound.has(prop.name)) continue\n finding(\n 'prop-unbound',\n `\"${prop.name}\" is declared and never bound. Every property fills a setting of the section: bind a ${prop.type} property to ${aiComponentKindBindsTo(prop.type ?? 'text')}.`,\n 'bindings',\n )\n }\n if (!bindings.length) {\n finding(\n 'nothing-proposed',\n 'Nothing in this section was proposed as a property. Name the values a page should be able to change.',\n 'bindings',\n )\n }\n if (violations.length) return { value: null, violations }\n return {\n value: {\n summary: cleanText(answer['summary'], SUMMARY_CHARS),\n props,\n bindings,\n },\n violations: [],\n }\n}\n\n/* ------------------------------------------------------------------ *\n * The request\n * ------------------------------------------------------------------ */\n\nconst INSTRUCTION_TEXT = [\n 'You are naming the properties a section should declare once it becomes a reusable component.',\n '',\n 'A person selected a section of a page they are editing. You are given the outline of that page — its elements, their settings and their text, shortened — and the id of the element they selected. Decide which of the values inside that selection a page placing the component should be able to change, and bind each one to the setting it fills.',\n '',\n 'Rules:',\n '- Only elements inside the selected section. Never bind anything outside it.',\n '- One property per thing a page changes, not one per element. A heading and the paragraph under it are two properties; the same heading bound twice is one.',\n '- Bind a property to a setting of its own kind: copy to text, a picture to a picture, an address to a link, an answer to a setting that lists answers.',\n `- An optional part gets a Yes / no property labeled \"Hide …\", bound to that part's \"${NODE_HIDE_IF_PROP}\". Never to the section itself.`,\n '- Leave alone what every page should show the same way: the layout, the styling, the labels that make the section what it is.',\n '- Do not invent defaults. The editor reads each value off the page as it writes the property in.',\n '',\n `Answer by calling ${AI_COMPONENT_SELECTION_TOOL_NAME} exactly once. A reply in prose cannot be used.`,\n].join('\\n')\n\n/** Byte-identical on every request, so the block caches. */\nexport const AI_COMPONENT_SELECTION_INSTRUCTIONS: AiSystemBlock[] = [\n { text: INSTRUCTION_TEXT, cacheBreakpoint: true },\n]\n\n/** The outline and the selection, as the question the model answers. */\nexport function aiComponentSelectionPrompt(\n context: AssistEditCanvasContext,\n selectedId: string,\n name: string,\n): string {\n const inside = aiComponentSelectionIds(context, selectedId)\n // The brief tier lies outside the selection, which is all this door binds.\n const lines = context.nodes.filter((node) => !node.brief).map((node) => {\n const parts = [\n `${inside.has(node.id) ? '*' : ' '} ${node.id}: ${node.componentId}`,\n node.name ? `name=${JSON.stringify(node.name)}` : '',\n node.parentId ? `in=${node.parentId}` : 'root',\n node.childCount ? `children=${node.childCount}` : '',\n node.props ? `settings=${JSON.stringify(node.props)}` : '',\n ]\n return parts.filter(Boolean).join(' ')\n })\n return [\n `The component will be called ${JSON.stringify(name)}.`,\n `The selected section is ${selectedId}; every line marked * is inside it.`,\n '',\n ...lines,\n ].join('\\n')\n}\n\n/** What the door answers on success. */\nexport interface AiComponentSelectionAnswer {\n edit: AssistEditProposal\n exchangeId: string | null\n}\n\n/**\n * The proposal for one selection, as the op the card applies. Exported for\n * the spec, which drives it with a recorded answer and never a live model.\n */\nexport function aiComponentSelectionOp(\n proposal: AiComponentSelectionProposal,\n selection: AssistEditCanvasNode,\n name: string,\n): AssistEditSaveAsComponentOp {\n return {\n op: 'saveAsComponent',\n nodeId: selection.id,\n componentId: selection.componentId,\n name,\n props: proposal.props,\n bindings: proposal.bindings,\n }\n}\n\nexport async function POST(request: Request): Promise<Response> {\n let body: Record<string, unknown> | null\n try {\n body = (await request.json()) as Record<string, unknown>\n } catch {\n body = null\n }\n const rawHost = body?.['hostId']\n const hostId = typeof rawHost === 'string' && rawHost ? rawHost : null\n const gate = await aiGateLadder(\n { request, orgId: String(body?.['orgId'] ?? ''), hostId },\n {\n feature: 'aiGenerative',\n releaseFlag: 'release_ai_generative',\n lockdownFeature: 'ai-generate',\n permission: 'ai.generate',\n rateLimit: RATE_LIMIT,\n },\n )\n if (gate instanceof Response) return gate\n // Nothing is spent until the model runs, so every exit before it hands the\n // reservation back.\n const release = () =>\n releaseAssistMessage(gate.firestore, gate.orgId, gate.reservation).catch(() => undefined)\n const refuse = async (error: string, status: number): Promise<Response> => {\n await release()\n return Response.json({ error }, { status })\n }\n\n if (!hostId) return refuse('Open the site this section is on before saving it.', 400)\n const document = assistEditDocumentOf(String(body?.['route'] ?? ''))\n if (!document) {\n return refuse('Open the page, component or layout this section is on.', 400)\n }\n const context = parseAssistEditContext(body?.['canvas'])\n if (!context) return refuse('The editor did not describe what is open.', 400)\n const name = cleanText(body?.['name'], NAME_CHARS)\n if (!name) return refuse('Give the component a name first.', 400)\n\n const selectedId = context.selectedId\n const refusal = aiComponentSelectionRefusal(context, selectedId)\n if (refusal) return refuse(refusal, 400)\n const selection = context.nodes.find((node) => node.id === selectedId)\n if (!selection) return refuse('Select the section you want to save as a component first.', 400)\n\n const target: AssistEditTarget = { hostId, ...document }\n const generation: AiCustomGenerationInput<AiComponentSelectionProposal> = {\n step: 'job.component',\n instructions: AI_COMPONENT_SELECTION_INSTRUCTIONS,\n // No site inventory: this step reads the open document and nothing else\n // of the site, and the disclosure says so.\n inventory: undefined,\n messages: [{ role: 'user', content: aiComponentSelectionPrompt(context, selection.id, name) }],\n tool: aiComponentSelectionTool(),\n maxTokens: AI_COMPONENT_SELECTION_MAX_TOKENS,\n ...(AI_ROUTING_TABLE['job.component'].thinking\n ? { thinking: AI_ROUTING_TABLE['job.component'].thinking }\n : {}),\n check: (answer) => checkAiComponentSelection(answer, context, selection.id),\n }\n let result: AiValidatedGeneration<AiComponentSelectionProposal>\n try {\n result = await runValidatedGeneration(AI_COMPONENT_SELECTION_KIND, generation)\n } catch (error) {\n await release()\n console.error('ai component proposal failed', { orgId: gate.orgId, hostId, error })\n return Response.json({ error: 'The component could not be proposed' }, { status: 500 })\n }\n\n if (result.status !== 'ok') {\n const error =\n result.status === 'refused'\n ? 'The assistant would not propose a component for this section.'\n : (result.message ??\n 'The assistant could not name properties for this section. Try a smaller part of it.')\n // The model ran, so the reservation is spent: it is not handed back.\n return Response.json({ error }, { status: 422 })\n }\n\n const op = aiComponentSelectionOp(result.value, selection, name)\n const edit: AssistEditProposal = {\n id: ASSIST_EDIT_ACTION_ID,\n summary: result.value.summary,\n ops: [op],\n diff: summarizeAssistEditOps([op]),\n dropped: [],\n target,\n }\n let exchangeId: string | null = null\n try {\n exchangeId = await recordAssistExchange(gate.firestore, gate.orgId, {\n uid: gate.uid,\n hostId,\n kind: 'component',\n // The person's own words for the component, and the proposal's line\n // back: the pair the signals page reads, and nothing off the page.\n question: name,\n answer: edit.summary,\n route: String(body?.['route'] ?? ''),\n model: result.model,\n tier: 'entitled',\n usage: result.usage,\n docsPaths: [],\n stopReason: result.stopReason,\n free: gate.reservation.free ?? null,\n editOps: edit.ops.length,\n })\n } catch (error) {\n // The proposal is the answer; a meter that could not be written is not a\n // reason to withhold it from the person who paid for it.\n console.error('ai component exchange not recorded', { orgId: gate.orgId, error })\n }\n return Response.json({ edit, exchangeId } satisfies AiComponentSelectionAnswer)\n}\n"],"names":["NODE_HIDE_IF_PROP","CANVAS_ROOT_ELEMENT_ID","ASSIST_EDIT_ACTION_ID","assistEditDocumentOf","summarizeAssistEditOps","AI_ROUTING_TABLE","aiComponentKindBindsTo","aiComponentPropBindsToField","aiComponentUnofferedAnswers","runValidatedGeneration","aiGateLadder","AI_PALETTE","AI_COMPONENT_MAX_PROPS","AI_COMPONENT_SELECTION_PROP_KINDS","aiComponentPropKindWords","recordAssistExchange","releaseAssistMessage","parseAssistEditContext","AI_COMPONENT_SELECTION_TOOL_NAME","AI_COMPONENT_SELECTION_KIND","AI_COMPONENT_SELECTION_MAX_TOKENS","RATE_LIMIT","key","limit","windowMs","NAME_CHARS","SUMMARY_CHARS","NAME_MAX","LABEL_MAX","HELP_MAX","PROP_NAME","FIELD_NAME","isRecord","value","Array","isArray","cleanText","out","char","String","code","charCodeAt","replace","trim","slice","aiComponentSelectionIds","context","selectedId","byParent","Map","node","nodes","parentId","set","get","id","found","Set","queue","length","shift","has","add","push","headingLevel","props","componentId","element","match","exec","Number","elementOf","aiComponentSelectionRefusal","described","map","selection","ids","childCount","filter","other","inside","Boolean","some","STRING","type","aiComponentSelectionTool","name","description","strict","inputSchema","additionalProperties","required","properties","summary","items","enum","label","options","bindings","nodeId","field","prop","HIDE_LABEL","paletteEntry","checkAiComponentSelection","answer","violations","finding","message","path","rule","paths","rawProps","rawBindings","byName","forEach","raw","index","test","includes","option","bound","propName","entry","binding","unoffered","join","INSTRUCTION_TEXT","AI_COMPONENT_SELECTION_INSTRUCTIONS","text","cacheBreakpoint","aiComponentSelectionPrompt","lines","brief","parts","JSON","stringify","aiComponentSelectionOp","proposal","op","POST","request","body","json","rawHost","hostId","gate","orgId","feature","releaseFlag","lockdownFeature","permission","rateLimit","Response","release","firestore","reservation","catch","undefined","refuse","error","status","document","refusal","find","target","generation","step","instructions","inventory","messages","role","content","tool","maxTokens","thinking","check","result","console","edit","ops","diff","dropped","exchangeId","uid","kind","question","route","model","tier","usage","docsPaths","stopReason","free","editOps"],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type AiPermissionVerdict } from '../model/ai-permissions';
|
|
18
|
+
/**
|
|
19
|
+
* The `aiPermissions` field of a body: a map of AI keys to booleans, or
|
|
20
|
+
* `false` for anything else. Keys outside the two and non-boolean values are
|
|
21
|
+
* refused rather than dropped, so a client that mis-spells a key learns so
|
|
22
|
+
* instead of saving nothing.
|
|
23
|
+
*/
|
|
24
|
+
export declare function readAiPermissionToggles(value: unknown): Partial<AiPermissionVerdict> | false;
|
|
25
|
+
/** `assist on, generate off` — the activity entry's words for a verdict. */
|
|
26
|
+
export declare function describeAiPermissions(verdict: AiPermissionVerdict): string;
|
|
27
|
+
/**
|
|
28
|
+
* `PATCH /api/ai/host-permissions` (AGL-2927, AGL-2984): a site
|
|
29
|
+
* collaborator's AI toggles, set from the site's collaborators card.
|
|
30
|
+
*
|
|
31
|
+
* The door is the collaborators card's own — a site admin, or an org member
|
|
32
|
+
* who manages people, on a site whose organization is not locked — and the
|
|
33
|
+
* write is core's per-site toggle, `setHostPermissions`, which stores the
|
|
34
|
+
* toggle on the member document the AI doors read and re-projects the site.
|
|
35
|
+
* The display roster carries the verdict beside it so the card renders
|
|
36
|
+
* without a second read.
|
|
37
|
+
*
|
|
38
|
+
* Recorded twice, like every other collaborator change: a sentence in the
|
|
39
|
+
* site's feed naming the state the toggles became, and one
|
|
40
|
+
* `org.permissions.changed` per key that moved, which this plugin's handler
|
|
41
|
+
* turns into the org feed's coded row. A toggle set to the value it already
|
|
42
|
+
* had moves nothing and raises nothing. A pending invite has no member
|
|
43
|
+
* document to carry a toggle, and becomes settable once it is accepted.
|
|
44
|
+
*/
|
|
45
|
+
declare function handler(request: Request): Promise<Response>;
|
|
46
|
+
export { handler as PATCH };
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { pluginPermissionChanges, pluginRequestFromWeb, runPluginEventHandlers } from "@aglyn/aglyn/server";
|
|
18
|
+
import { emailUnverifiedResponse, firebaseAdmin, getOrgForHost, isImpersonationSession, lockdownRefusal, logHostActivity, setHostPermissions } from "@aglyn/tenant-data-admin";
|
|
19
|
+
import { invalidIdTokenResponse } from "@aglyn/tenant-data-admin/server/id-token-refusal";
|
|
20
|
+
import { resolveOrgPermissions } from "@aglyn/tenant-runtime/org-permissions";
|
|
21
|
+
import { AI_PERMISSION_KEYS, aiPermissionsOf, isAiPermission } from "../model/ai-permissions.js";
|
|
22
|
+
/**
|
|
23
|
+
* The `aiPermissions` field of a body: a map of AI keys to booleans, or
|
|
24
|
+
* `false` for anything else. Keys outside the two and non-boolean values are
|
|
25
|
+
* refused rather than dropped, so a client that mis-spells a key learns so
|
|
26
|
+
* instead of saving nothing.
|
|
27
|
+
*/ export function readAiPermissionToggles(value) {
|
|
28
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
29
|
+
const accepted = {};
|
|
30
|
+
for (const [key, entry] of Object.entries(value)){
|
|
31
|
+
if (!isAiPermission(key) || typeof entry !== 'boolean') return false;
|
|
32
|
+
accepted[key] = entry;
|
|
33
|
+
}
|
|
34
|
+
return Object.keys(accepted).length ? accepted : false;
|
|
35
|
+
}
|
|
36
|
+
/** `assist on, generate off` — the activity entry's words for a verdict. */ export function describeAiPermissions(verdict) {
|
|
37
|
+
return `assist ${verdict['ai.use'] ? 'on' : 'off'}, generate ${verdict['ai.generate'] ? 'on' : 'off'}`;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* `PATCH /api/ai/host-permissions` (AGL-2927, AGL-2984): a site
|
|
41
|
+
* collaborator's AI toggles, set from the site's collaborators card.
|
|
42
|
+
*
|
|
43
|
+
* The door is the collaborators card's own — a site admin, or an org member
|
|
44
|
+
* who manages people, on a site whose organization is not locked — and the
|
|
45
|
+
* write is core's per-site toggle, `setHostPermissions`, which stores the
|
|
46
|
+
* toggle on the member document the AI doors read and re-projects the site.
|
|
47
|
+
* The display roster carries the verdict beside it so the card renders
|
|
48
|
+
* without a second read.
|
|
49
|
+
*
|
|
50
|
+
* Recorded twice, like every other collaborator change: a sentence in the
|
|
51
|
+
* site's feed naming the state the toggles became, and one
|
|
52
|
+
* `org.permissions.changed` per key that moved, which this plugin's handler
|
|
53
|
+
* turns into the org feed's coded row. A toggle set to the value it already
|
|
54
|
+
* had moves nothing and raises nothing. A pending invite has no member
|
|
55
|
+
* document to carry a toggle, and becomes settable once it is accepted.
|
|
56
|
+
*/ async function handler(request) {
|
|
57
|
+
var _headers_authorization, _ref;
|
|
58
|
+
const { method, body, headers: rawHeaders } = await pluginRequestFromWeb(request);
|
|
59
|
+
if (method !== 'PATCH') {
|
|
60
|
+
return Response.json({
|
|
61
|
+
error: 'Method not allowed'
|
|
62
|
+
}, {
|
|
63
|
+
status: 405
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const headers = rawHeaders;
|
|
67
|
+
const authorization = (_headers_authorization = headers.authorization) != null ? _headers_authorization : '';
|
|
68
|
+
const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
|
|
69
|
+
if (!idToken) return Response.json({
|
|
70
|
+
error: 'Unauthenticated'
|
|
71
|
+
}, {
|
|
72
|
+
status: 401
|
|
73
|
+
});
|
|
74
|
+
const hostId = String((_ref = body == null ? void 0 : body.hostId) != null ? _ref : '');
|
|
75
|
+
if (!hostId) return Response.json({
|
|
76
|
+
error: 'Missing hostId'
|
|
77
|
+
}, {
|
|
78
|
+
status: 400
|
|
79
|
+
});
|
|
80
|
+
try {
|
|
81
|
+
var _ref1, _ref2;
|
|
82
|
+
const app = firebaseAdmin.app();
|
|
83
|
+
const decoded = await app.auth().verifyIdToken(idToken);
|
|
84
|
+
if (!decoded.email_verified && !isImpersonationSession(decoded)) {
|
|
85
|
+
return emailUnverifiedResponse();
|
|
86
|
+
}
|
|
87
|
+
const firestore = app.firestore();
|
|
88
|
+
const hostRef = firestore.collection('hosts').doc(hostId);
|
|
89
|
+
const host = (await hostRef.get()).data();
|
|
90
|
+
const memberRole = ((_ref1 = host == null ? void 0 : host['memberRoles']) != null ? _ref1 : {})[decoded.uid];
|
|
91
|
+
const orgPermissions = await resolveOrgPermissions(decoded.uid, {
|
|
92
|
+
hostId
|
|
93
|
+
});
|
|
94
|
+
if (!host || memberRole !== 'admin' && !orgPermissions.permissions.manageMembers) {
|
|
95
|
+
return Response.json({
|
|
96
|
+
error: 'Not a site admin'
|
|
97
|
+
}, {
|
|
98
|
+
status: 403
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const resolved = await getOrgForHost(hostId);
|
|
102
|
+
if (!resolved) {
|
|
103
|
+
return Response.json({
|
|
104
|
+
error: 'This site has no organization yet'
|
|
105
|
+
}, {
|
|
106
|
+
status: 409
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
const { orgId, org } = resolved;
|
|
110
|
+
const locked = await lockdownRefusal({
|
|
111
|
+
request,
|
|
112
|
+
staff: decoded['staff'] === true,
|
|
113
|
+
uid: decoded.uid,
|
|
114
|
+
org,
|
|
115
|
+
host
|
|
116
|
+
});
|
|
117
|
+
if (locked) return locked;
|
|
118
|
+
const toggles = readAiPermissionToggles(body == null ? void 0 : body.aiPermissions);
|
|
119
|
+
if (toggles === false) {
|
|
120
|
+
return Response.json({
|
|
121
|
+
error: `aiPermissions must map ${AI_PERMISSION_KEYS.join(' and ')} to booleans`
|
|
122
|
+
}, {
|
|
123
|
+
status: 400
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const memberId = String((_ref2 = body == null ? void 0 : body.memberId) != null ? _ref2 : '');
|
|
127
|
+
if (!memberId) return Response.json({
|
|
128
|
+
error: 'Missing member'
|
|
129
|
+
}, {
|
|
130
|
+
status: 400
|
|
131
|
+
});
|
|
132
|
+
const membersRef = hostRef.collection('members');
|
|
133
|
+
const member = (await membersRef.doc(memberId).get()).data();
|
|
134
|
+
if (!member) return Response.json({
|
|
135
|
+
error: 'Member not found'
|
|
136
|
+
}, {
|
|
137
|
+
status: 404
|
|
138
|
+
});
|
|
139
|
+
if (!member['uid']) {
|
|
140
|
+
return Response.json({
|
|
141
|
+
error: 'AI access is set once the invite is accepted'
|
|
142
|
+
}, {
|
|
143
|
+
status: 409
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
const { before, after } = await setHostPermissions({
|
|
147
|
+
orgId,
|
|
148
|
+
uid: String(member['uid']),
|
|
149
|
+
hostId,
|
|
150
|
+
permissions: toggles
|
|
151
|
+
});
|
|
152
|
+
const verdict = aiPermissionsOf(after);
|
|
153
|
+
await membersRef.doc(memberId).update({
|
|
154
|
+
aiPermissions: verdict
|
|
155
|
+
});
|
|
156
|
+
const actor = {
|
|
157
|
+
uid: decoded.uid,
|
|
158
|
+
email: decoded.email ? String(decoded.email) : null
|
|
159
|
+
};
|
|
160
|
+
await logHostActivity(hostId, actor, `Changed member AI access to ${describeAiPermissions(verdict)}`, _extends({
|
|
161
|
+
type: 'member',
|
|
162
|
+
id: memberId
|
|
163
|
+
}, member['email'] ? {
|
|
164
|
+
name: String(member['email'])
|
|
165
|
+
} : {}));
|
|
166
|
+
const subjectName = [
|
|
167
|
+
host['displayName'],
|
|
168
|
+
member['email']
|
|
169
|
+
].map((value)=>typeof value === 'string' ? value.trim() : '').filter(Boolean).join(' · ');
|
|
170
|
+
for (const change of pluginPermissionChanges(before, after)){
|
|
171
|
+
if (!isAiPermission(change.permission)) continue;
|
|
172
|
+
await runPluginEventHandlers('org.permissions.changed', {
|
|
173
|
+
orgId,
|
|
174
|
+
actor,
|
|
175
|
+
subject: {
|
|
176
|
+
type: 'host',
|
|
177
|
+
id: hostId,
|
|
178
|
+
name: subjectName
|
|
179
|
+
},
|
|
180
|
+
permission: change.permission,
|
|
181
|
+
granted: change.granted
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return Response.json({
|
|
185
|
+
ok: true,
|
|
186
|
+
aiPermissions: verdict
|
|
187
|
+
}, {
|
|
188
|
+
status: 200
|
|
189
|
+
});
|
|
190
|
+
} catch (error) {
|
|
191
|
+
// An unverifiable credential is a 401, not a fault of ours (AGL-1993).
|
|
192
|
+
const unauthenticated = invalidIdTokenResponse(error);
|
|
193
|
+
if (unauthenticated) return unauthenticated;
|
|
194
|
+
console.error('[ai/host-permissions]', error);
|
|
195
|
+
return Response.json({
|
|
196
|
+
error: 'Member operation failed'
|
|
197
|
+
}, {
|
|
198
|
+
status: 500
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
export { handler as PATCH };
|
|
203
|
+
|
|
204
|
+
//# sourceMappingURL=ai-host-permissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/server/ai-host-permissions.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n pluginPermissionChanges,\n pluginRequestFromWeb,\n runPluginEventHandlers,\n} from '@aglyn/aglyn/server'\nimport {\n emailUnverifiedResponse,\n firebaseAdmin,\n getOrgForHost,\n isImpersonationSession,\n lockdownRefusal,\n logHostActivity,\n setHostPermissions,\n} from '@aglyn/tenant-data-admin'\nimport { invalidIdTokenResponse } from '@aglyn/tenant-data-admin/server/id-token-refusal'\nimport { resolveOrgPermissions } from '@aglyn/tenant-runtime/org-permissions'\nimport {\n AI_PERMISSION_KEYS,\n aiPermissionsOf,\n isAiPermission,\n type AiPermissionVerdict,\n} from '../model/ai-permissions'\n\n/**\n * The `aiPermissions` field of a body: a map of AI keys to booleans, or\n * `false` for anything else. Keys outside the two and non-boolean values are\n * refused rather than dropped, so a client that mis-spells a key learns so\n * instead of saving nothing.\n */\nexport function readAiPermissionToggles(value: unknown): Partial<AiPermissionVerdict> | false {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false\n const accepted: Partial<AiPermissionVerdict> = {}\n for (const [key, entry] of Object.entries(value as Record<string, unknown>)) {\n if (!isAiPermission(key) || typeof entry !== 'boolean') return false\n accepted[key] = entry\n }\n return Object.keys(accepted).length ? accepted : false\n}\n\n/** `assist on, generate off` — the activity entry's words for a verdict. */\nexport function describeAiPermissions(verdict: AiPermissionVerdict): string {\n return `assist ${verdict['ai.use'] ? 'on' : 'off'}, generate ${\n verdict['ai.generate'] ? 'on' : 'off'\n }`\n}\n\n/**\n * `PATCH /api/ai/host-permissions` (AGL-2927, AGL-2984): a site\n * collaborator's AI toggles, set from the site's collaborators card.\n *\n * The door is the collaborators card's own — a site admin, or an org member\n * who manages people, on a site whose organization is not locked — and the\n * write is core's per-site toggle, `setHostPermissions`, which stores the\n * toggle on the member document the AI doors read and re-projects the site.\n * The display roster carries the verdict beside it so the card renders\n * without a second read.\n *\n * Recorded twice, like every other collaborator change: a sentence in the\n * site's feed naming the state the toggles became, and one\n * `org.permissions.changed` per key that moved, which this plugin's handler\n * turns into the org feed's coded row. A toggle set to the value it already\n * had moves nothing and raises nothing. A pending invite has no member\n * document to carry a toggle, and becomes settable once it is accepted.\n */\nasync function handler(request: Request): Promise<Response> {\n const { method, body, headers: rawHeaders } = await pluginRequestFromWeb(request)\n if (method !== 'PATCH') {\n return Response.json({ error: 'Method not allowed' }, { status: 405 })\n }\n const headers = rawHeaders as Partial<Record<string, string>>\n const authorization = headers.authorization ?? ''\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) return Response.json({ error: 'Unauthenticated' }, { status: 401 })\n const hostId = String(body?.hostId ?? '')\n if (!hostId) return Response.json({ error: 'Missing hostId' }, { status: 400 })\n\n try {\n const app = firebaseAdmin.app()\n const decoded = await app.auth().verifyIdToken(idToken)\n if (!decoded.email_verified && !isImpersonationSession(decoded)) {\n return emailUnverifiedResponse()\n }\n const firestore = app.firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n const host = (await hostRef.get()).data()\n const memberRole = (host?.['memberRoles'] ?? {})[decoded.uid]\n const orgPermissions = await resolveOrgPermissions(decoded.uid, { hostId })\n if (!host || (memberRole !== 'admin' && !orgPermissions.permissions.manageMembers)) {\n return Response.json({ error: 'Not a site admin' }, { status: 403 })\n }\n const resolved = await getOrgForHost(hostId)\n if (!resolved) {\n return Response.json({ error: 'This site has no organization yet' }, { status: 409 })\n }\n const { orgId, org } = resolved\n const locked = await lockdownRefusal({\n request,\n staff: decoded['staff'] === true,\n uid: decoded.uid,\n org,\n host,\n })\n if (locked) return locked\n\n const toggles = readAiPermissionToggles(body?.aiPermissions)\n if (toggles === false) {\n return Response.json(\n { error: `aiPermissions must map ${AI_PERMISSION_KEYS.join(' and ')} to booleans` },\n { status: 400 },\n )\n }\n const memberId = String(body?.memberId ?? '')\n if (!memberId) return Response.json({ error: 'Missing member' }, { status: 400 })\n const membersRef = hostRef.collection('members')\n const member = (await membersRef.doc(memberId).get()).data()\n if (!member) return Response.json({ error: 'Member not found' }, { status: 404 })\n if (!member['uid']) {\n return Response.json(\n { error: 'AI access is set once the invite is accepted' },\n { status: 409 },\n )\n }\n\n const { before, after } = await setHostPermissions({\n orgId,\n uid: String(member['uid']),\n hostId,\n permissions: toggles,\n })\n const verdict = aiPermissionsOf(after)\n await membersRef.doc(memberId).update({ aiPermissions: verdict })\n const actor = {\n uid: decoded.uid,\n email: decoded.email ? String(decoded.email) : null,\n }\n await logHostActivity(\n hostId,\n actor,\n `Changed member AI access to ${describeAiPermissions(verdict)}`,\n {\n type: 'member',\n id: memberId,\n ...(member['email'] ? { name: String(member['email']) } : {}),\n },\n )\n const subjectName = [host['displayName'], member['email']]\n .map((value) => (typeof value === 'string' ? value.trim() : ''))\n .filter(Boolean)\n .join(' · ')\n for (const change of pluginPermissionChanges(before, after)) {\n if (!isAiPermission(change.permission)) continue\n await runPluginEventHandlers('org.permissions.changed', {\n orgId,\n actor,\n subject: { type: 'host', id: hostId, name: subjectName },\n permission: change.permission,\n granted: change.granted,\n })\n }\n return Response.json({ ok: true, aiPermissions: verdict }, { status: 200 })\n } catch (error) {\n // An unverifiable credential is a 401, not a fault of ours (AGL-1993).\n const unauthenticated = invalidIdTokenResponse(error)\n if (unauthenticated) return unauthenticated\n console.error('[ai/host-permissions]', error)\n return Response.json({ error: 'Member operation failed' }, { status: 500 })\n }\n}\n\nexport { handler as PATCH }\n"],"names":["pluginPermissionChanges","pluginRequestFromWeb","runPluginEventHandlers","emailUnverifiedResponse","firebaseAdmin","getOrgForHost","isImpersonationSession","lockdownRefusal","logHostActivity","setHostPermissions","invalidIdTokenResponse","resolveOrgPermissions","AI_PERMISSION_KEYS","aiPermissionsOf","isAiPermission","readAiPermissionToggles","value","Array","isArray","accepted","key","entry","Object","entries","keys","length","describeAiPermissions","verdict","handler","request","headers","method","body","rawHeaders","Response","json","error","status","authorization","idToken","startsWith","slice","undefined","hostId","String","app","decoded","auth","verifyIdToken","email_verified","firestore","hostRef","collection","doc","host","get","data","memberRole","uid","orgPermissions","permissions","manageMembers","resolved","orgId","org","locked","staff","toggles","aiPermissions","join","memberId","membersRef","member","before","after","update","actor","email","type","id","name","subjectName","map","trim","filter","Boolean","change","permission","subject","granted","ok","unauthenticated","console","PATCH"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,uBAAuB,EACvBC,oBAAoB,EACpBC,sBAAsB,QACjB,sBAAqB;AAC5B,SACEC,uBAAuB,EACvBC,aAAa,EACbC,aAAa,EACbC,sBAAsB,EACtBC,eAAe,EACfC,eAAe,EACfC,kBAAkB,QACb,2BAA0B;AACjC,SAASC,sBAAsB,QAAQ,mDAAkD;AACzF,SAASC,qBAAqB,QAAQ,wCAAuC;AAC7E,SACEC,kBAAkB,EAClBC,eAAe,EACfC,cAAc,QAET,6BAAyB;AAEhC;;;;;CAKC,GACD,OAAO,SAASC,wBAAwBC,KAAc;IACpD,IAAI,CAACA,SAAS,OAAOA,UAAU,YAAYC,MAAMC,OAAO,CAACF,QAAQ,OAAO;IACxE,MAAMG,WAAyC,CAAC;IAChD,KAAK,MAAM,CAACC,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAACP,OAAmC;QAC3E,IAAI,CAACF,eAAeM,QAAQ,OAAOC,UAAU,WAAW,OAAO;QAC/DF,QAAQ,CAACC,IAAI,GAAGC;IAClB;IACA,OAAOC,OAAOE,IAAI,CAACL,UAAUM,MAAM,GAAGN,WAAW;AACnD;AAEA,0EAA0E,GAC1E,OAAO,SAASO,sBAAsBC,OAA4B;IAChE,OAAO,CAAC,OAAO,EAAEA,OAAO,CAAC,SAAS,GAAG,OAAO,MAAM,WAAW,EAC3DA,OAAO,CAAC,cAAc,GAAG,OAAO,OAChC;AACJ;AAEA;;;;;;;;;;;;;;;;;CAiBC,GACD,eAAeC,QAAQC,OAAgB;QAMfC;IALtB,MAAM,EAAEC,MAAM,EAAEC,IAAI,EAAEF,SAASG,UAAU,EAAE,GAAG,MAAMhC,qBAAqB4B;IACzE,IAAIE,WAAW,SAAS;QACtB,OAAOG,SAASC,IAAI,CAAC;YAAEC,OAAO;QAAqB,GAAG;YAAEC,QAAQ;QAAI;IACtE;IACA,MAAMP,UAAUG;IAChB,MAAMK,iBAAgBR,yBAAAA,QAAQQ,aAAa,YAArBR,yBAAyB;IAC/C,MAAMS,UAAUD,cAAcE,UAAU,CAAC,aACrCF,cAAcG,KAAK,CAAC,UAAUhB,MAAM,IACpCiB;IACJ,IAAI,CAACH,SAAS,OAAOL,SAASC,IAAI,CAAC;QAAEC,OAAO;IAAkB,GAAG;QAAEC,QAAQ;IAAI;IAC/E,MAAMM,SAASC,eAAOZ,wBAAAA,KAAMW,MAAM,mBAAI;IACtC,IAAI,CAACA,QAAQ,OAAOT,SAASC,IAAI,CAAC;QAAEC,OAAO;IAAiB,GAAG;QAAEC,QAAQ;IAAI;IAE7E,IAAI;;QACF,MAAMQ,MAAMzC,cAAcyC,GAAG;QAC7B,MAAMC,UAAU,MAAMD,IAAIE,IAAI,GAAGC,aAAa,CAACT;QAC/C,IAAI,CAACO,QAAQG,cAAc,IAAI,CAAC3C,uBAAuBwC,UAAU;YAC/D,OAAO3C;QACT;QACA,MAAM+C,YAAYL,IAAIK,SAAS;QAC/B,MAAMC,UAAUD,UAAUE,UAAU,CAAC,SAASC,GAAG,CAACV;QAClD,MAAMW,OAAO,AAAC,CAAA,MAAMH,QAAQI,GAAG,EAAC,EAAGC,IAAI;QACvC,MAAMC,aAAa,UAACH,wBAAAA,IAAM,CAAC,cAAc,oBAAI,CAAC,EAAE,CAACR,QAAQY,GAAG,CAAC;QAC7D,MAAMC,iBAAiB,MAAMhD,sBAAsBmC,QAAQY,GAAG,EAAE;YAAEf;QAAO;QACzE,IAAI,CAACW,QAASG,eAAe,WAAW,CAACE,eAAeC,WAAW,CAACC,aAAa,EAAG;YAClF,OAAO3B,SAASC,IAAI,CAAC;gBAAEC,OAAO;YAAmB,GAAG;gBAAEC,QAAQ;YAAI;QACpE;QACA,MAAMyB,WAAW,MAAMzD,cAAcsC;QACrC,IAAI,CAACmB,UAAU;YACb,OAAO5B,SAASC,IAAI,CAAC;gBAAEC,OAAO;YAAoC,GAAG;gBAAEC,QAAQ;YAAI;QACrF;QACA,MAAM,EAAE0B,KAAK,EAAEC,GAAG,EAAE,GAAGF;QACvB,MAAMG,SAAS,MAAM1D,gBAAgB;YACnCsB;YACAqC,OAAOpB,OAAO,CAAC,QAAQ,KAAK;YAC5BY,KAAKZ,QAAQY,GAAG;YAChBM;YACAV;QACF;QACA,IAAIW,QAAQ,OAAOA;QAEnB,MAAME,UAAUpD,wBAAwBiB,wBAAAA,KAAMoC,aAAa;QAC3D,IAAID,YAAY,OAAO;YACrB,OAAOjC,SAASC,IAAI,CAClB;gBAAEC,OAAO,CAAC,uBAAuB,EAAExB,mBAAmByD,IAAI,CAAC,SAAS,YAAY,CAAC;YAAC,GAClF;gBAAEhC,QAAQ;YAAI;QAElB;QACA,MAAMiC,WAAW1B,gBAAOZ,wBAAAA,KAAMsC,QAAQ,oBAAI;QAC1C,IAAI,CAACA,UAAU,OAAOpC,SAASC,IAAI,CAAC;YAAEC,OAAO;QAAiB,GAAG;YAAEC,QAAQ;QAAI;QAC/E,MAAMkC,aAAapB,QAAQC,UAAU,CAAC;QACtC,MAAMoB,SAAS,AAAC,CAAA,MAAMD,WAAWlB,GAAG,CAACiB,UAAUf,GAAG,EAAC,EAAGC,IAAI;QAC1D,IAAI,CAACgB,QAAQ,OAAOtC,SAASC,IAAI,CAAC;YAAEC,OAAO;QAAmB,GAAG;YAAEC,QAAQ;QAAI;QAC/E,IAAI,CAACmC,MAAM,CAAC,MAAM,EAAE;YAClB,OAAOtC,SAASC,IAAI,CAClB;gBAAEC,OAAO;YAA+C,GACxD;gBAAEC,QAAQ;YAAI;QAElB;QAEA,MAAM,EAAEoC,MAAM,EAAEC,KAAK,EAAE,GAAG,MAAMjE,mBAAmB;YACjDsD;YACAL,KAAKd,OAAO4B,MAAM,CAAC,MAAM;YACzB7B;YACAiB,aAAaO;QACf;QACA,MAAMxC,UAAUd,gBAAgB6D;QAChC,MAAMH,WAAWlB,GAAG,CAACiB,UAAUK,MAAM,CAAC;YAAEP,eAAezC;QAAQ;QAC/D,MAAMiD,QAAQ;YACZlB,KAAKZ,QAAQY,GAAG;YAChBmB,OAAO/B,QAAQ+B,KAAK,GAAGjC,OAAOE,QAAQ+B,KAAK,IAAI;QACjD;QACA,MAAMrE,gBACJmC,QACAiC,OACA,CAAC,4BAA4B,EAAElD,sBAAsBC,UAAU,EAC/D;YACEmD,MAAM;YACNC,IAAIT;WACAE,MAAM,CAAC,QAAQ,GAAG;YAAEQ,MAAMpC,OAAO4B,MAAM,CAAC,QAAQ;QAAE,IAAI,CAAC;QAG/D,MAAMS,cAAc;YAAC3B,IAAI,CAAC,cAAc;YAAEkB,MAAM,CAAC,QAAQ;SAAC,CACvDU,GAAG,CAAC,CAAClE,QAAW,OAAOA,UAAU,WAAWA,MAAMmE,IAAI,KAAK,IAC3DC,MAAM,CAACC,SACPhB,IAAI,CAAC;QACR,KAAK,MAAMiB,UAAUtF,wBAAwByE,QAAQC,OAAQ;YAC3D,IAAI,CAAC5D,eAAewE,OAAOC,UAAU,GAAG;YACxC,MAAMrF,uBAAuB,2BAA2B;gBACtD6D;gBACAa;gBACAY,SAAS;oBAAEV,MAAM;oBAAQC,IAAIpC;oBAAQqC,MAAMC;gBAAY;gBACvDM,YAAYD,OAAOC,UAAU;gBAC7BE,SAASH,OAAOG,OAAO;YACzB;QACF;QACA,OAAOvD,SAASC,IAAI,CAAC;YAAEuD,IAAI;YAAMtB,eAAezC;QAAQ,GAAG;YAAEU,QAAQ;QAAI;IAC3E,EAAE,OAAOD,OAAO;QACd,uEAAuE;QACvE,MAAMuD,kBAAkBjF,uBAAuB0B;QAC/C,IAAIuD,iBAAiB,OAAOA;QAC5BC,QAAQxD,KAAK,CAAC,yBAAyBA;QACvC,OAAOF,SAASC,IAAI,CAAC;YAAEC,OAAO;QAA0B,GAAG;YAAEC,QAAQ;QAAI;IAC3E;AACF;AAEA,SAAST,WAAWiE,KAAK,GAAE"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { AglynOrgMember } from '@aglyn/aglyn/foundation/definitions/organization.types';
|
|
18
|
+
import { type AiInsightRecord } from '../model/ai-insight';
|
|
19
|
+
/** Whether this member may read this answer; `member` is `null` for a caller with no membership. */
|
|
20
|
+
export declare function aiInsightAnswerReadable(record: Pick<AiInsightRecord, 'createdBy' | 'hostId' | 'surface'>, caller: {
|
|
21
|
+
uid: string;
|
|
22
|
+
staff: boolean;
|
|
23
|
+
member: Partial<AglynOrgMember> | null;
|
|
24
|
+
}): boolean;
|
|
25
|
+
export declare function GET(request: Request, context: {
|
|
26
|
+
params: Promise<{
|
|
27
|
+
jobId: string;
|
|
28
|
+
}>;
|
|
29
|
+
}): Promise<Response>;
|
|
30
|
+
export declare const dynamic = "force-dynamic";
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ // lockdown-423: via libs/plugins/ai/src/lib/server/ai-jobs-gate.ts
|
|
18
|
+
// The GET climbs `aiJobsGate`, whose lockdown rung is the verdict.
|
|
19
|
+
import { hostRoleFor } from "@aglyn/aglyn/app-utils/organizations";
|
|
20
|
+
import { AI_INSIGHTS_COLLECTION } from "../model/ai-insight.js";
|
|
21
|
+
import { aiJobsGate } from "./ai-jobs-gate.js";
|
|
22
|
+
/**
|
|
23
|
+
* One insight answer (AGL-2915): `GET /api/ai/insights/{jobId}?orgId=`.
|
|
24
|
+
*
|
|
25
|
+
* The figures an insight job read are not on the job document, which every
|
|
26
|
+
* member of the workspace may read: they are in the org's `aiInsights`
|
|
27
|
+
* collection under the job's id, which no rule lets a client read, and this
|
|
28
|
+
* door is the one way to them. It climbs the jobs read gate — the release flag, the `aiGenerative`
|
|
29
|
+
* entitlement and the lockdown verdict — and then answers only:
|
|
30
|
+
*
|
|
31
|
+
* - the member who asked the question, while they still reach its site;
|
|
32
|
+
* - for a weekly digest, which no one asked, any member who reaches its site;
|
|
33
|
+
* - staff.
|
|
34
|
+
*
|
|
35
|
+
* Anyone else is told the answer does not exist, as a job they cannot read is.
|
|
36
|
+
*/ const ID = /^[A-Za-z0-9_-]{1,128}$/;
|
|
37
|
+
const notFound = ()=>Response.json({
|
|
38
|
+
error: 'Not found'
|
|
39
|
+
}, {
|
|
40
|
+
status: 404
|
|
41
|
+
});
|
|
42
|
+
/** Whether this member may read this answer; `member` is `null` for a caller with no membership. */ export function aiInsightAnswerReadable(record, caller) {
|
|
43
|
+
if (caller.staff) return true;
|
|
44
|
+
if (!caller.member) return false;
|
|
45
|
+
const reachesSite = record.hostId ? hostRoleFor(caller.member, record.hostId) !== null : true;
|
|
46
|
+
if (!reachesSite) return false;
|
|
47
|
+
return record.createdBy === caller.uid || record.surface === 'digest';
|
|
48
|
+
}
|
|
49
|
+
export async function GET(request, context) {
|
|
50
|
+
var _url_searchParams_get;
|
|
51
|
+
const url = new URL(request.url);
|
|
52
|
+
const gate = await aiJobsGate(request, (_url_searchParams_get = url.searchParams.get('orgId')) != null ? _url_searchParams_get : '');
|
|
53
|
+
if (gate instanceof Response) return gate;
|
|
54
|
+
const { jobId } = await context.params;
|
|
55
|
+
if (!ID.test(jobId)) return notFound();
|
|
56
|
+
const orgRef = gate.firestore.collection('orgs').doc(gate.orgId);
|
|
57
|
+
const [answer, member] = await Promise.all([
|
|
58
|
+
orgRef.collection(AI_INSIGHTS_COLLECTION).doc(jobId).get(),
|
|
59
|
+
orgRef.collection('members').doc(gate.uid).get()
|
|
60
|
+
]);
|
|
61
|
+
if (!answer.exists) return notFound();
|
|
62
|
+
const record = answer.data();
|
|
63
|
+
const readable = aiInsightAnswerReadable(record, {
|
|
64
|
+
uid: gate.uid,
|
|
65
|
+
staff: gate.staff,
|
|
66
|
+
member: member.exists ? member.data() : null
|
|
67
|
+
});
|
|
68
|
+
if (!readable) return notFound();
|
|
69
|
+
// The expiry is the retention clock's, not the reader's.
|
|
70
|
+
const wire = _extends({}, record);
|
|
71
|
+
delete wire['expiresAt'];
|
|
72
|
+
return Response.json({
|
|
73
|
+
answer: wire
|
|
74
|
+
}, {
|
|
75
|
+
status: 200,
|
|
76
|
+
headers: {
|
|
77
|
+
'Cache-Control': 'no-store'
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
export const dynamic = 'force-dynamic';
|
|
82
|
+
|
|
83
|
+
//# sourceMappingURL=ai-insight-answer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/server/ai-insight-answer.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// lockdown-423: via libs/plugins/ai/src/lib/server/ai-jobs-gate.ts\n// The GET climbs `aiJobsGate`, whose lockdown rung is the verdict.\n\nimport { hostRoleFor } from '@aglyn/aglyn/app-utils/organizations'\nimport type { AglynOrgMember } from '@aglyn/aglyn/foundation/definitions/organization.types'\nimport {\n AI_INSIGHTS_COLLECTION,\n type AiInsightAnswerWire,\n type AiInsightRecord,\n} from '../model/ai-insight'\nimport { aiJobsGate } from './ai-jobs-gate'\n\n/**\n * One insight answer (AGL-2915): `GET /api/ai/insights/{jobId}?orgId=`.\n *\n * The figures an insight job read are not on the job document, which every\n * member of the workspace may read: they are in the org's `aiInsights`\n * collection under the job's id, which no rule lets a client read, and this\n * door is the one way to them. It climbs the jobs read gate — the release flag, the `aiGenerative`\n * entitlement and the lockdown verdict — and then answers only:\n *\n * - the member who asked the question, while they still reach its site;\n * - for a weekly digest, which no one asked, any member who reaches its site;\n * - staff.\n *\n * Anyone else is told the answer does not exist, as a job they cannot read is.\n */\n\nconst ID = /^[A-Za-z0-9_-]{1,128}$/\n\nconst notFound = () => Response.json({ error: 'Not found' }, { status: 404 })\n\n/** Whether this member may read this answer; `member` is `null` for a caller with no membership. */\nexport function aiInsightAnswerReadable(\n record: Pick<AiInsightRecord, 'createdBy' | 'hostId' | 'surface'>,\n caller: { uid: string; staff: boolean; member: Partial<AglynOrgMember> | null },\n): boolean {\n if (caller.staff) return true\n if (!caller.member) return false\n const reachesSite = record.hostId ? hostRoleFor(caller.member, record.hostId) !== null : true\n if (!reachesSite) return false\n return record.createdBy === caller.uid || record.surface === 'digest'\n}\n\nexport async function GET(\n request: Request,\n context: { params: Promise<{ jobId: string }> },\n): Promise<Response> {\n const url = new URL(request.url)\n const gate = await aiJobsGate(request, url.searchParams.get('orgId') ?? '')\n if (gate instanceof Response) return gate\n const { jobId } = await context.params\n if (!ID.test(jobId)) return notFound()\n const orgRef = gate.firestore.collection('orgs').doc(gate.orgId)\n const [answer, member] = await Promise.all([\n orgRef.collection(AI_INSIGHTS_COLLECTION).doc(jobId).get(),\n orgRef.collection('members').doc(gate.uid).get(),\n ])\n if (!answer.exists) return notFound()\n const record = answer.data() as AiInsightRecord & { expiresAt?: unknown }\n const readable = aiInsightAnswerReadable(record, {\n uid: gate.uid,\n staff: gate.staff,\n member: member.exists ? (member.data() as Partial<AglynOrgMember>) : null,\n })\n if (!readable) return notFound()\n // The expiry is the retention clock's, not the reader's.\n const wire: Record<string, unknown> = { ...record }\n delete wire['expiresAt']\n return Response.json(\n { answer: wire as unknown as AiInsightAnswerWire },\n { status: 200, headers: { 'Cache-Control': 'no-store' } },\n )\n}\n\nexport const dynamic = 'force-dynamic'\n"],"names":["hostRoleFor","AI_INSIGHTS_COLLECTION","aiJobsGate","ID","notFound","Response","json","error","status","aiInsightAnswerReadable","record","caller","staff","member","reachesSite","hostId","createdBy","uid","surface","GET","request","context","url","URL","gate","searchParams","get","jobId","params","test","orgRef","firestore","collection","doc","orgId","answer","Promise","all","exists","data","readable","wire","headers","dynamic"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,mEAAmE;AACnE,mEAAmE;AAEnE,SAASA,WAAW,QAAQ,uCAAsC;AAElE,SACEC,sBAAsB,QAGjB,yBAAqB;AAC5B,SAASC,UAAU,QAAQ,oBAAgB;AAE3C;;;;;;;;;;;;;;CAcC,GAED,MAAMC,KAAK;AAEX,MAAMC,WAAW,IAAMC,SAASC,IAAI,CAAC;QAAEC,OAAO;IAAY,GAAG;QAAEC,QAAQ;IAAI;AAE3E,kGAAkG,GAClG,OAAO,SAASC,wBACdC,MAAiE,EACjEC,MAA+E;IAE/E,IAAIA,OAAOC,KAAK,EAAE,OAAO;IACzB,IAAI,CAACD,OAAOE,MAAM,EAAE,OAAO;IAC3B,MAAMC,cAAcJ,OAAOK,MAAM,GAAGf,YAAYW,OAAOE,MAAM,EAAEH,OAAOK,MAAM,MAAM,OAAO;IACzF,IAAI,CAACD,aAAa,OAAO;IACzB,OAAOJ,OAAOM,SAAS,KAAKL,OAAOM,GAAG,IAAIP,OAAOQ,OAAO,KAAK;AAC/D;AAEA,OAAO,eAAeC,IACpBC,OAAgB,EAChBC,OAA+C;QAGRC;IADvC,MAAMA,MAAM,IAAIC,IAAIH,QAAQE,GAAG;IAC/B,MAAME,OAAO,MAAMtB,WAAWkB,UAASE,wBAAAA,IAAIG,YAAY,CAACC,GAAG,CAAC,oBAArBJ,wBAAiC;IACxE,IAAIE,gBAAgBnB,UAAU,OAAOmB;IACrC,MAAM,EAAEG,KAAK,EAAE,GAAG,MAAMN,QAAQO,MAAM;IACtC,IAAI,CAACzB,GAAG0B,IAAI,CAACF,QAAQ,OAAOvB;IAC5B,MAAM0B,SAASN,KAAKO,SAAS,CAACC,UAAU,CAAC,QAAQC,GAAG,CAACT,KAAKU,KAAK;IAC/D,MAAM,CAACC,QAAQtB,OAAO,GAAG,MAAMuB,QAAQC,GAAG,CAAC;QACzCP,OAAOE,UAAU,CAAC/B,wBAAwBgC,GAAG,CAACN,OAAOD,GAAG;QACxDI,OAAOE,UAAU,CAAC,WAAWC,GAAG,CAACT,KAAKP,GAAG,EAAES,GAAG;KAC/C;IACD,IAAI,CAACS,OAAOG,MAAM,EAAE,OAAOlC;IAC3B,MAAMM,SAASyB,OAAOI,IAAI;IAC1B,MAAMC,WAAW/B,wBAAwBC,QAAQ;QAC/CO,KAAKO,KAAKP,GAAG;QACbL,OAAOY,KAAKZ,KAAK;QACjBC,QAAQA,OAAOyB,MAAM,GAAIzB,OAAO0B,IAAI,KAAiC;IACvE;IACA,IAAI,CAACC,UAAU,OAAOpC;IACtB,yDAAyD;IACzD,MAAMqC,OAAgC,aAAK/B;IAC3C,OAAO+B,IAAI,CAAC,YAAY;IACxB,OAAOpC,SAASC,IAAI,CAClB;QAAE6B,QAAQM;IAAuC,GACjD;QAAEjC,QAAQ;QAAKkC,SAAS;YAAE,iBAAiB;QAAW;IAAE;AAE5D;AAEA,OAAO,MAAMC,UAAU,gBAAe"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The weekly insights' scheduled route (AGL-2915):
|
|
19
|
+
* `POST /api/admin/ai-insights-digest`.
|
|
20
|
+
*
|
|
21
|
+
* Cloud Scheduler calls it at 06:00 and 14:00 UTC through
|
|
22
|
+
* `consoleAiInsightsDigest` in `cloud/functions`, on the console's
|
|
23
|
+
* `CRON_SECRET`: 501 while the secret is unset, 401 without it. Monday's
|
|
24
|
+
* first run makes the week's digests, which the AI jobs beat then writes, and
|
|
25
|
+
* every run after it delivers what is written — see `ai-insight-digest.ts`.
|
|
26
|
+
* `/api/admin/` is the path because the console's edge lets the cron secret's
|
|
27
|
+
* header through there. It stamps `ai-insights-digest` for
|
|
28
|
+
* `/api/health/crons` on every authorized call.
|
|
29
|
+
*/
|
|
30
|
+
export declare function POST(request: Request): Promise<Response>;
|