@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,294 @@
|
|
|
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 gate carries the org verdict; the handler adds the site's own below,
|
|
19
|
+
// with the host document in hand.
|
|
20
|
+
import { checkEntitlement, createResourceUid, encodeStoredNodes, hostRoleCanWrite } from "@aglyn/aglyn/server";
|
|
21
|
+
import { pageContentRootId } from "@aglyn/aglyn/app-utils/page-markdown";
|
|
22
|
+
import { decodeStoredNodes } from "@aglyn/aglyn/app-utils/stored-nodes";
|
|
23
|
+
import { lockdownRefusal } from "@aglyn/tenant-data-admin/server/lockdown";
|
|
24
|
+
import { logAiSeoApplied } from "../activity/ai-activity.js";
|
|
25
|
+
import { aiJobSummary, getAiJob, recordAiJobApplied, writeAiJobAudit } from "../jobs/ai-jobs.js";
|
|
26
|
+
import { aiSeoAuditView, aiSeoJobTarget } from "../model/ai-seo.js";
|
|
27
|
+
import { applyAiSeoContentFixes } from "../runtime/seo-content-fixes.js";
|
|
28
|
+
import { aiJobsGate } from "./ai-jobs-gate.js";
|
|
29
|
+
/**
|
|
30
|
+
* Apply a site audit (AGL-2910): `POST /api/ai/seo/apply { orgId, hostId, jobId }`.
|
|
31
|
+
*
|
|
32
|
+
* "Apply all" does two things and neither changes what the published site
|
|
33
|
+
* serves.
|
|
34
|
+
*
|
|
35
|
+
* - **Content fixes open a NEW version per page.** The page's published
|
|
36
|
+
* version is copied, the fixes are applied to the copy — an image
|
|
37
|
+
* description, the page's one main heading — and the copy is stored as a
|
|
38
|
+
* new unpublished version. The published version and the screen's
|
|
39
|
+
* `versionId` are never touched: a person opens the draft, looks, and
|
|
40
|
+
* publishes it through the door that already exists.
|
|
41
|
+
* - **Listing values are STAGED, not written.** A page's search title and
|
|
42
|
+
* description are served straight from the screen document, so writing
|
|
43
|
+
* them would publish them. The apply records which pages have values
|
|
44
|
+
* waiting; the page's SEO card offers them, and its Save SEO is the write.
|
|
45
|
+
*
|
|
46
|
+
* The site-wide proposals — structured data and the agent guidance — are the
|
|
47
|
+
* site SEO form's to save, and the card puts them there as unsaved edits; this
|
|
48
|
+
* door never writes the host document.
|
|
49
|
+
*
|
|
50
|
+
* Idempotent per job: a page this job already opened a version for keeps
|
|
51
|
+
* that version, so a second press opens nothing new.
|
|
52
|
+
*
|
|
53
|
+
* ## The rungs
|
|
54
|
+
*
|
|
55
|
+
* The jobs read gate (membership, the release flag, `aiGenerative`, the org
|
|
56
|
+
* lockdown), then the site: it must be the job's and the org's, the caller
|
|
57
|
+
* must hold a role that writes the site's content — the role creating a
|
|
58
|
+
* version asks for — and the site's own lockdown verdict must admit a write.
|
|
59
|
+
* Nothing here spends a token, so there is no reservation.
|
|
60
|
+
*/ const ID_CHARS = /^[A-Za-z0-9_-]{1,100}$/;
|
|
61
|
+
/** The version history sentence the versions route gives, for a plan without it. */ export const AI_SEO_APPLY_VERSIONING_COPY = 'Version history requires a Pro plan — see Billing to upgrade';
|
|
62
|
+
const str = (value)=>typeof value === 'string' ? value.trim() : '';
|
|
63
|
+
/**
|
|
64
|
+
* Open the versions and record the apply. Exported for the spec, which drives
|
|
65
|
+
* it against a Firestore double and proves what it leaves alone.
|
|
66
|
+
*/ export async function applyAiSeoAudit(firestore, input) {
|
|
67
|
+
var _input_now, _ref, _ref1;
|
|
68
|
+
var _input_job_applied, _input_job_applied1;
|
|
69
|
+
const now = (_input_now = input.now) != null ? _input_now : new Date();
|
|
70
|
+
const hostRef = firestore.collection('hosts').doc(input.hostId);
|
|
71
|
+
const opened = _extends({}, (_ref = (_input_job_applied = input.job.applied) == null ? void 0 : _input_job_applied.versions) != null ? _ref : {});
|
|
72
|
+
const staged = new Set((_ref1 = (_input_job_applied1 = input.job.applied) == null ? void 0 : _input_job_applied1.staged) != null ? _ref1 : []);
|
|
73
|
+
const names = new Map(input.view.report.pages.map((page)=>[
|
|
74
|
+
page.screenId,
|
|
75
|
+
page.name
|
|
76
|
+
]));
|
|
77
|
+
const versions = [];
|
|
78
|
+
const skipped = [];
|
|
79
|
+
for (const fix of Object.values(input.view.fixes)){
|
|
80
|
+
var _names_get;
|
|
81
|
+
const screenId = fix.screenId;
|
|
82
|
+
const name = (_names_get = names.get(screenId)) != null ? _names_get : screenId;
|
|
83
|
+
if (Object.keys(fix.values).length) staged.add(screenId);
|
|
84
|
+
if (!fix.content.length) continue;
|
|
85
|
+
const screenRef = hostRef.collection('screens').doc(screenId);
|
|
86
|
+
const earlier = opened[screenId];
|
|
87
|
+
if (earlier && (await screenRef.collection('versions').doc(earlier).get()).exists) {
|
|
88
|
+
versions.push({
|
|
89
|
+
screenId,
|
|
90
|
+
versionId: earlier,
|
|
91
|
+
name,
|
|
92
|
+
fixes: 0
|
|
93
|
+
});
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (!input.versioning) {
|
|
97
|
+
skipped.push({
|
|
98
|
+
screenId,
|
|
99
|
+
reason: AI_SEO_APPLY_VERSIONING_COPY
|
|
100
|
+
});
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const screenSnapshot = await screenRef.get();
|
|
104
|
+
if (!screenSnapshot.exists || screenSnapshot.get('deletedAt')) {
|
|
105
|
+
skipped.push({
|
|
106
|
+
screenId,
|
|
107
|
+
reason: 'The page no longer exists.'
|
|
108
|
+
});
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const publishedId = str(screenSnapshot.get('versionId'));
|
|
112
|
+
const source = publishedId ? await screenRef.collection('versions').doc(publishedId).get() : null;
|
|
113
|
+
const sourceData = (source == null ? void 0 : source.exists) ? source.data() : null;
|
|
114
|
+
const nodes = sourceData ? decodeStoredNodes(sourceData['nodes']) : null;
|
|
115
|
+
if (!sourceData || !nodes) {
|
|
116
|
+
skipped.push({
|
|
117
|
+
screenId,
|
|
118
|
+
reason: 'The page has no published version to start from.'
|
|
119
|
+
});
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const rootId = typeof sourceData['rootId'] === 'string' ? sourceData['rootId'] : undefined;
|
|
123
|
+
const result = applyAiSeoContentFixes(nodes, fix.content, pageContentRootId(nodes, rootId));
|
|
124
|
+
if (!result.applied.length) {
|
|
125
|
+
var _ref2;
|
|
126
|
+
var _result_skipped_;
|
|
127
|
+
skipped.push({
|
|
128
|
+
screenId,
|
|
129
|
+
reason: (_ref2 = (_result_skipped_ = result.skipped[0]) == null ? void 0 : _result_skipped_.reason) != null ? _ref2 : 'Nothing on the page needed changing.'
|
|
130
|
+
});
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const versionId = createResourceUid();
|
|
134
|
+
// Compressed at rest, as every write of a version's nodes is.
|
|
135
|
+
const packed = encodeStoredNodes(result.nodes);
|
|
136
|
+
await screenRef.collection('versions').doc(versionId).create(_extends({}, sourceData, {
|
|
137
|
+
nodes: packed ? Buffer.from(packed) : result.nodes,
|
|
138
|
+
displayName: 'SEO fixes',
|
|
139
|
+
createdAt: now,
|
|
140
|
+
updatedAt: now,
|
|
141
|
+
createdBy: input.uid
|
|
142
|
+
}));
|
|
143
|
+
opened[screenId] = versionId;
|
|
144
|
+
versions.push({
|
|
145
|
+
screenId,
|
|
146
|
+
versionId,
|
|
147
|
+
name,
|
|
148
|
+
fixes: result.applied.length
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
const applied = {
|
|
152
|
+
// The Admin SDK stores a `Date` as the timestamp the type describes.
|
|
153
|
+
at: now,
|
|
154
|
+
by: input.uid,
|
|
155
|
+
versions: opened,
|
|
156
|
+
staged: [
|
|
157
|
+
...staged
|
|
158
|
+
]
|
|
159
|
+
};
|
|
160
|
+
const job = await recordAiJobApplied(firestore, input.orgId, input.job.$id, applied, now);
|
|
161
|
+
return {
|
|
162
|
+
job,
|
|
163
|
+
versions,
|
|
164
|
+
staged: [
|
|
165
|
+
...staged
|
|
166
|
+
],
|
|
167
|
+
skipped
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
export async function POST(request) {
|
|
171
|
+
var _body_orgId, _host_memberRoles, _job_outputs, _decoded_email;
|
|
172
|
+
let body;
|
|
173
|
+
try {
|
|
174
|
+
var _ref;
|
|
175
|
+
body = (_ref = await request.json()) != null ? _ref : {};
|
|
176
|
+
} catch (unused) {
|
|
177
|
+
body = {};
|
|
178
|
+
}
|
|
179
|
+
const gate = await aiJobsGate(request, String((_body_orgId = body['orgId']) != null ? _body_orgId : ''));
|
|
180
|
+
if (gate instanceof Response) return gate;
|
|
181
|
+
const hostId = str(body['hostId']);
|
|
182
|
+
const jobId = str(body['jobId']);
|
|
183
|
+
if (!ID_CHARS.test(hostId) || !ID_CHARS.test(jobId)) {
|
|
184
|
+
return Response.json({
|
|
185
|
+
error: 'Name the site and the audit to apply'
|
|
186
|
+
}, {
|
|
187
|
+
status: 400
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
const { firestore, orgId, uid, staff, org, decoded } = gate;
|
|
191
|
+
const hostSnapshot = await firestore.collection('hosts').doc(hostId).get();
|
|
192
|
+
const host = hostSnapshot.exists ? hostSnapshot.data() : null;
|
|
193
|
+
if (!host || host['orgId'] !== orgId) {
|
|
194
|
+
return Response.json({
|
|
195
|
+
error: 'Unknown site'
|
|
196
|
+
}, {
|
|
197
|
+
status: 404
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const role = ((_host_memberRoles = host['memberRoles']) != null ? _host_memberRoles : {})[uid];
|
|
201
|
+
if (!staff && !hostRoleCanWrite(role)) {
|
|
202
|
+
return Response.json({
|
|
203
|
+
error: 'Editing requires the editor role'
|
|
204
|
+
}, {
|
|
205
|
+
status: 403
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
const locked = await lockdownRefusal({
|
|
209
|
+
request,
|
|
210
|
+
staff,
|
|
211
|
+
uid,
|
|
212
|
+
org: org,
|
|
213
|
+
host: host
|
|
214
|
+
});
|
|
215
|
+
if (locked) return locked;
|
|
216
|
+
const job = await getAiJob(firestore, orgId, jobId);
|
|
217
|
+
if (!job || job.kind !== 'seo' || job.hostId !== hostId || aiSeoJobTarget(job.inputs) !== 'site') {
|
|
218
|
+
return Response.json({
|
|
219
|
+
error: 'Unknown audit'
|
|
220
|
+
}, {
|
|
221
|
+
status: 404
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
if (job.status !== 'done') {
|
|
225
|
+
return Response.json({
|
|
226
|
+
error: 'The audit is still running — apply it once it has finished'
|
|
227
|
+
}, {
|
|
228
|
+
status: 409
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
const view = aiSeoAuditView((_job_outputs = job.outputs) != null ? _job_outputs : []);
|
|
232
|
+
if (!view) {
|
|
233
|
+
return Response.json({
|
|
234
|
+
error: 'This audit has nothing to apply'
|
|
235
|
+
}, {
|
|
236
|
+
status: 409
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
const result = await applyAiSeoAudit(firestore, {
|
|
240
|
+
orgId,
|
|
241
|
+
hostId,
|
|
242
|
+
job,
|
|
243
|
+
view,
|
|
244
|
+
uid,
|
|
245
|
+
versioning: checkEntitlement(org, 'versioning')
|
|
246
|
+
});
|
|
247
|
+
const opened = result.versions.filter((entry)=>entry.fixes > 0);
|
|
248
|
+
await writeAiJobAudit(firestore, {
|
|
249
|
+
action: 'ai.job.apply',
|
|
250
|
+
actorUid: uid,
|
|
251
|
+
actorEmail: (_decoded_email = decoded.email) != null ? _decoded_email : null,
|
|
252
|
+
orgId,
|
|
253
|
+
jobId,
|
|
254
|
+
after: {
|
|
255
|
+
hostId,
|
|
256
|
+
versions: Object.fromEntries(opened.map((entry)=>[
|
|
257
|
+
entry.screenId,
|
|
258
|
+
entry.versionId
|
|
259
|
+
])),
|
|
260
|
+
staged: result.staged
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
if (opened.length || result.staged.length) {
|
|
264
|
+
var _decoded_email1;
|
|
265
|
+
await logAiSeoApplied(orgId, {
|
|
266
|
+
uid,
|
|
267
|
+
email: (_decoded_email1 = decoded.email) != null ? _decoded_email1 : null
|
|
268
|
+
}, {
|
|
269
|
+
jobId,
|
|
270
|
+
hostId,
|
|
271
|
+
hostName: str(host['displayName']) || null,
|
|
272
|
+
versions: opened.length,
|
|
273
|
+
staged: result.staged.length
|
|
274
|
+
}).catch((error)=>console.error('ai seo apply activity failed', {
|
|
275
|
+
orgId,
|
|
276
|
+
jobId,
|
|
277
|
+
error
|
|
278
|
+
}));
|
|
279
|
+
}
|
|
280
|
+
return Response.json({
|
|
281
|
+
job: aiJobSummary(result.job),
|
|
282
|
+
versions: result.versions,
|
|
283
|
+
staged: result.staged,
|
|
284
|
+
skipped: result.skipped
|
|
285
|
+
}, {
|
|
286
|
+
status: 200,
|
|
287
|
+
headers: {
|
|
288
|
+
'Cache-Control': 'no-store'
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
export const dynamic = 'force-dynamic';
|
|
293
|
+
|
|
294
|
+
//# sourceMappingURL=ai-seo-apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/server/ai-seo-apply.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 gate carries the org verdict; the handler adds the site's own below,\n// with the host document in hand.\n\nimport {\n checkEntitlement,\n createResourceUid,\n encodeStoredNodes,\n hostRoleCanWrite,\n} from '@aglyn/aglyn/server'\nimport { pageContentRootId } from '@aglyn/aglyn/app-utils/page-markdown'\nimport { decodeStoredNodes } from '@aglyn/aglyn/app-utils/stored-nodes'\nimport { lockdownRefusal } from '@aglyn/tenant-data-admin/server/lockdown'\nimport { logAiSeoApplied } from '../activity/ai-activity'\nimport {\n aiJobSummary,\n getAiJob,\n recordAiJobApplied,\n writeAiJobAudit,\n} from '../jobs/ai-jobs'\nimport type { AiJob, AiJobApplied } from '../model/ai-jobs.types'\nimport { aiSeoAuditView, aiSeoJobTarget, type AiSeoAuditView } from '../model/ai-seo'\nimport { applyAiSeoContentFixes } from '../runtime/seo-content-fixes'\nimport { aiJobsGate } from './ai-jobs-gate'\n\n/**\n * Apply a site audit (AGL-2910): `POST /api/ai/seo/apply { orgId, hostId, jobId }`.\n *\n * \"Apply all\" does two things and neither changes what the published site\n * serves.\n *\n * - **Content fixes open a NEW version per page.** The page's published\n * version is copied, the fixes are applied to the copy — an image\n * description, the page's one main heading — and the copy is stored as a\n * new unpublished version. The published version and the screen's\n * `versionId` are never touched: a person opens the draft, looks, and\n * publishes it through the door that already exists.\n * - **Listing values are STAGED, not written.** A page's search title and\n * description are served straight from the screen document, so writing\n * them would publish them. The apply records which pages have values\n * waiting; the page's SEO card offers them, and its Save SEO is the write.\n *\n * The site-wide proposals — structured data and the agent guidance — are the\n * site SEO form's to save, and the card puts them there as unsaved edits; this\n * door never writes the host document.\n *\n * Idempotent per job: a page this job already opened a version for keeps\n * that version, so a second press opens nothing new.\n *\n * ## The rungs\n *\n * The jobs read gate (membership, the release flag, `aiGenerative`, the org\n * lockdown), then the site: it must be the job's and the org's, the caller\n * must hold a role that writes the site's content — the role creating a\n * version asks for — and the site's own lockdown verdict must admit a write.\n * Nothing here spends a token, so there is no reservation.\n */\n\nconst ID_CHARS = /^[A-Za-z0-9_-]{1,100}$/\n\n/** The version history sentence the versions route gives, for a plan without it. */\nexport const AI_SEO_APPLY_VERSIONING_COPY =\n 'Version history requires a Pro plan — see Billing to upgrade'\n\nexport interface AiSeoApplyInput {\n orgId: string\n hostId: string\n job: AiJob\n view: AiSeoAuditView\n uid: string\n /** Whether the org's plan keeps more than one version of a page. */\n versioning: boolean\n now?: Date\n}\n\nexport interface AiSeoApplyResult {\n job: AiJob\n /** The version each page's content fixes went into; `fixes` is 0 for one opened by an earlier apply. */\n versions: Array<{ screenId: string; versionId: string; name: string; fixes: number }>\n /** Pages whose listing values wait in their SEO card. */\n staged: string[]\n skipped: Array<{ screenId: string; reason: string }>\n}\n\nconst str = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\n/**\n * Open the versions and record the apply. Exported for the spec, which drives\n * it against a Firestore double and proves what it leaves alone.\n */\nexport async function applyAiSeoAudit(\n firestore: FirebaseFirestore.Firestore,\n input: AiSeoApplyInput,\n): Promise<AiSeoApplyResult> {\n const now = input.now ?? new Date()\n const hostRef = firestore.collection('hosts').doc(input.hostId)\n const opened = { ...(input.job.applied?.versions ?? {}) }\n const staged = new Set(input.job.applied?.staged ?? [])\n const names = new Map(input.view.report.pages.map((page) => [page.screenId, page.name]))\n const versions: AiSeoApplyResult['versions'] = []\n const skipped: AiSeoApplyResult['skipped'] = []\n\n for (const fix of Object.values(input.view.fixes)) {\n const screenId = fix.screenId\n const name = names.get(screenId) ?? screenId\n if (Object.keys(fix.values).length) staged.add(screenId)\n if (!fix.content.length) continue\n const screenRef = hostRef.collection('screens').doc(screenId)\n\n const earlier = opened[screenId]\n if (earlier && (await screenRef.collection('versions').doc(earlier).get()).exists) {\n versions.push({ screenId, versionId: earlier, name, fixes: 0 })\n continue\n }\n if (!input.versioning) {\n skipped.push({ screenId, reason: AI_SEO_APPLY_VERSIONING_COPY })\n continue\n }\n const screenSnapshot = await screenRef.get()\n if (!screenSnapshot.exists || screenSnapshot.get('deletedAt')) {\n skipped.push({ screenId, reason: 'The page no longer exists.' })\n continue\n }\n const publishedId = str(screenSnapshot.get('versionId'))\n const source = publishedId ? await screenRef.collection('versions').doc(publishedId).get() : null\n const sourceData = source?.exists ? (source.data() as Record<string, unknown>) : null\n const nodes = sourceData ? decodeStoredNodes<Record<string, unknown>>(sourceData['nodes']) : null\n if (!sourceData || !nodes) {\n skipped.push({ screenId, reason: 'The page has no published version to start from.' })\n continue\n }\n const rootId = typeof sourceData['rootId'] === 'string' ? sourceData['rootId'] : undefined\n const result = applyAiSeoContentFixes(\n nodes,\n fix.content,\n pageContentRootId(nodes as never, rootId),\n )\n if (!result.applied.length) {\n skipped.push({ screenId, reason: result.skipped[0]?.reason ?? 'Nothing on the page needed changing.' })\n continue\n }\n const versionId = createResourceUid()\n // Compressed at rest, as every write of a version's nodes is.\n const packed = encodeStoredNodes(result.nodes)\n await screenRef\n .collection('versions')\n .doc(versionId)\n .create({\n ...sourceData,\n nodes: packed ? Buffer.from(packed) : result.nodes,\n displayName: 'SEO fixes',\n createdAt: now,\n updatedAt: now,\n createdBy: input.uid,\n })\n opened[screenId] = versionId\n versions.push({ screenId, versionId, name, fixes: result.applied.length })\n }\n\n const applied: AiJobApplied = {\n // The Admin SDK stores a `Date` as the timestamp the type describes.\n at: now as unknown as AiJobApplied['at'],\n by: input.uid,\n versions: opened,\n staged: [...staged],\n }\n const job = await recordAiJobApplied(firestore, input.orgId, input.job.$id, applied, now)\n return { job, versions, staged: [...staged], skipped }\n}\n\nexport async function POST(request: Request): Promise<Response> {\n let body: Record<string, unknown>\n try {\n body = ((await request.json()) ?? {}) as Record<string, unknown>\n } catch {\n body = {}\n }\n const gate = await aiJobsGate(request, String(body['orgId'] ?? ''))\n if (gate instanceof Response) return gate\n const hostId = str(body['hostId'])\n const jobId = str(body['jobId'])\n if (!ID_CHARS.test(hostId) || !ID_CHARS.test(jobId)) {\n return Response.json({ error: 'Name the site and the audit to apply' }, { status: 400 })\n }\n const { firestore, orgId, uid, staff, org, decoded } = gate\n\n const hostSnapshot = await firestore.collection('hosts').doc(hostId).get()\n const host = hostSnapshot.exists ? (hostSnapshot.data() as Record<string, unknown>) : null\n if (!host || host['orgId'] !== orgId) {\n return Response.json({ error: 'Unknown site' }, { status: 404 })\n }\n const role = ((host['memberRoles'] ?? {}) as Record<string, unknown>)[uid]\n if (!staff && !hostRoleCanWrite(role)) {\n return Response.json({ error: 'Editing requires the editor role' }, { status: 403 })\n }\n const locked = await lockdownRefusal({\n request,\n staff,\n uid,\n org: org as Record<string, unknown>,\n host: host as never,\n })\n if (locked) return locked\n\n const job = await getAiJob(firestore, orgId, jobId)\n if (!job || job.kind !== 'seo' || job.hostId !== hostId || aiSeoJobTarget(job.inputs) !== 'site') {\n return Response.json({ error: 'Unknown audit' }, { status: 404 })\n }\n if (job.status !== 'done') {\n return Response.json({ error: 'The audit is still running — apply it once it has finished' }, { status: 409 })\n }\n const view = aiSeoAuditView(job.outputs ?? [])\n if (!view) {\n return Response.json({ error: 'This audit has nothing to apply' }, { status: 409 })\n }\n\n const result = await applyAiSeoAudit(firestore, {\n orgId,\n hostId,\n job,\n view,\n uid,\n versioning: checkEntitlement(org, 'versioning'),\n })\n const opened = result.versions.filter((entry) => entry.fixes > 0)\n await writeAiJobAudit(firestore, {\n action: 'ai.job.apply',\n actorUid: uid,\n actorEmail: decoded.email ?? null,\n orgId,\n jobId,\n after: {\n hostId,\n versions: Object.fromEntries(opened.map((entry) => [entry.screenId, entry.versionId])),\n staged: result.staged,\n },\n })\n if (opened.length || result.staged.length) {\n await logAiSeoApplied(orgId, { uid, email: decoded.email ?? null }, {\n jobId,\n hostId,\n hostName: str(host['displayName']) || null,\n versions: opened.length,\n staged: result.staged.length,\n }).catch((error) => console.error('ai seo apply activity failed', { orgId, jobId, error }))\n }\n return Response.json(\n {\n job: aiJobSummary(result.job),\n versions: result.versions,\n staged: result.staged,\n skipped: result.skipped,\n },\n { status: 200, headers: { 'Cache-Control': 'no-store' } },\n )\n}\n\nexport const dynamic = 'force-dynamic'\n"],"names":["checkEntitlement","createResourceUid","encodeStoredNodes","hostRoleCanWrite","pageContentRootId","decodeStoredNodes","lockdownRefusal","logAiSeoApplied","aiJobSummary","getAiJob","recordAiJobApplied","writeAiJobAudit","aiSeoAuditView","aiSeoJobTarget","applyAiSeoContentFixes","aiJobsGate","ID_CHARS","AI_SEO_APPLY_VERSIONING_COPY","str","value","trim","applyAiSeoAudit","firestore","input","now","Date","hostRef","collection","doc","hostId","opened","job","applied","versions","staged","Set","names","Map","view","report","pages","map","page","screenId","name","skipped","fix","Object","values","fixes","get","keys","length","add","content","screenRef","earlier","exists","push","versionId","versioning","reason","screenSnapshot","publishedId","source","sourceData","data","nodes","rootId","undefined","result","packed","create","Buffer","from","displayName","createdAt","updatedAt","createdBy","uid","at","by","orgId","$id","POST","request","body","host","decoded","json","gate","String","Response","jobId","test","error","status","staff","org","hostSnapshot","role","locked","kind","inputs","outputs","filter","entry","action","actorUid","actorEmail","email","after","fromEntries","hostName","catch","console","headers","dynamic"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
declare function handler(request: Request): Promise<Response>;
|
|
18
|
+
export { handler as GET };
|
|
@@ -0,0 +1,220 @@
|
|
|
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
|
+
*/ import { csvCell, pluginRequestFromWeb } from "@aglyn/aglyn/server";
|
|
17
|
+
import { emailUnverifiedResponse, firebaseAdmin, isImpersonationSession, memberHasOrgPermission, resolveOrgMembership } from "@aglyn/tenant-data-admin";
|
|
18
|
+
import { aiUsageMonthKeys, aiUsageMonthWithinRetention, aiUsageShare } from "../model/ai-usage-by-user.js";
|
|
19
|
+
import { readOrgAiUsageByUser, readUserAiUsageMonths } from "../usage/ai-usage-by-user.js";
|
|
20
|
+
import { FieldValue } from "firebase-admin/firestore";
|
|
21
|
+
import { invalidIdTokenResponse } from "@aglyn/tenant-data-admin/server/id-token-refusal";
|
|
22
|
+
import { AI_USAGE_CSV_HEADER, AI_USAGE_EXPORT_ROWS_HEADER, aiUsageCsvLine, toAiUsageWireRow } from "../usage/ai-usage-wire.js";
|
|
23
|
+
// lockdown-423: exempt — a READ-ONLY usage report that writes nothing to the
|
|
24
|
+
// workspace, the posture of ai/billing/credits beside it: a billing-locked
|
|
25
|
+
// owner working out who spent the credits needs this table more, not less.
|
|
26
|
+
/**
|
|
27
|
+
* WHO IS GENERATING WHAT, PER SITE (AGL-2928) — the customer's read of the
|
|
28
|
+
* per-user AI usage rollup.
|
|
29
|
+
*
|
|
30
|
+
* ## Three questions, one route
|
|
31
|
+
*
|
|
32
|
+
* - **The month's table** (`orgId`, `month`): every current member with a
|
|
33
|
+
* month document, dearest first, with each person's share of the
|
|
34
|
+
* workspace's spend. Gated on `billing.view`, the Usage page's gate, and
|
|
35
|
+
* served as JSON or — `format=csv` — as a streamed file.
|
|
36
|
+
* - **One person** (`uid`): that person's months, newest first, for the
|
|
37
|
+
* member detail card. The subject reads their own; anyone else needs
|
|
38
|
+
* `billing.view` or `org.auditLog`, which is the team page's gate and the
|
|
39
|
+
* pair the security rules admit for the same documents.
|
|
40
|
+
* - **One site** (`hostId`): the table with each row's credits ON THAT SITE
|
|
41
|
+
* beside it, for the site's collaborators card — the agency question,
|
|
42
|
+
* "which client site's collaborators spend".
|
|
43
|
+
*
|
|
44
|
+
* ## No dollar figure crosses this boundary
|
|
45
|
+
*
|
|
46
|
+
* The month document carries `estCostUsd`, our provider bill. The rows here
|
|
47
|
+
* carry credits and a SHARE, and the share is computed on the dollars
|
|
48
|
+
* server-side (`aiUsageShare`) so it is exact — a roster's credits each round
|
|
49
|
+
* up, the org's round once — without the dollars ever being serialized.
|
|
50
|
+
*
|
|
51
|
+
* ## The month bound
|
|
52
|
+
*
|
|
53
|
+
* `month` must be inside the retention window (`aiUsageMonthKeys`): a
|
|
54
|
+
* request for an older month is refused rather than answered empty, because
|
|
55
|
+
* "nothing for that month" and "that month has been reaped" must not read
|
|
56
|
+
* the same on a report somebody exports.
|
|
57
|
+
*/ /** The most months one person's card is handed. */ const MAX_USER_MONTHS = 13;
|
|
58
|
+
const json = (body, status)=>Response.json(body, {
|
|
59
|
+
status
|
|
60
|
+
});
|
|
61
|
+
async function handler(request) {
|
|
62
|
+
var _headers_authorization, _params_orgId, _params_month, _params_uid, _params_hostId, _params_format;
|
|
63
|
+
const { method, query, headers: rawHeaders } = await pluginRequestFromWeb(request);
|
|
64
|
+
const headers = rawHeaders;
|
|
65
|
+
if (method !== 'GET') return json({
|
|
66
|
+
error: 'Method not allowed'
|
|
67
|
+
}, 405);
|
|
68
|
+
const authorization = (_headers_authorization = headers.authorization) != null ? _headers_authorization : '';
|
|
69
|
+
const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
|
|
70
|
+
if (!idToken) return json({
|
|
71
|
+
error: 'Unauthenticated'
|
|
72
|
+
}, 401);
|
|
73
|
+
const params = query != null ? query : {};
|
|
74
|
+
const orgId = String((_params_orgId = params['orgId']) != null ? _params_orgId : '').trim();
|
|
75
|
+
if (!orgId) return json({
|
|
76
|
+
error: 'Missing orgId'
|
|
77
|
+
}, 400);
|
|
78
|
+
const now = new Date();
|
|
79
|
+
const months = aiUsageMonthKeys(now);
|
|
80
|
+
const month = String((_params_month = params['month']) != null ? _params_month : months[0]).trim();
|
|
81
|
+
if (!aiUsageMonthWithinRetention(month, now)) {
|
|
82
|
+
return json({
|
|
83
|
+
error: `AI usage is kept for ${months.length} months; ` + `ask for one of ${months[months.length - 1]} through ${months[0]}.`
|
|
84
|
+
}, 400);
|
|
85
|
+
}
|
|
86
|
+
const subjectUid = String((_params_uid = params['uid']) != null ? _params_uid : '').trim();
|
|
87
|
+
const hostId = String((_params_hostId = params['hostId']) != null ? _params_hostId : '').trim();
|
|
88
|
+
const format = String((_params_format = params['format']) != null ? _params_format : 'json').trim();
|
|
89
|
+
try {
|
|
90
|
+
const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
|
|
91
|
+
if (!decoded.email_verified && !isImpersonationSession(decoded)) {
|
|
92
|
+
return emailUnverifiedResponse();
|
|
93
|
+
}
|
|
94
|
+
const staff = decoded['staff'] === true;
|
|
95
|
+
const membership = await resolveOrgMembership(decoded.uid, orgId);
|
|
96
|
+
const member = membership == null ? void 0 : membership.member;
|
|
97
|
+
if (!member && !staff) return json({
|
|
98
|
+
error: 'Not found'
|
|
99
|
+
}, 404);
|
|
100
|
+
const firestore = firebaseAdmin.app().firestore();
|
|
101
|
+
if (subjectUid) {
|
|
102
|
+
var _params_limit;
|
|
103
|
+
// One person. Their own months are theirs to read; anyone else's need
|
|
104
|
+
// the Usage page's permission or the team page's.
|
|
105
|
+
const own = subjectUid === decoded.uid && Boolean(member);
|
|
106
|
+
if (!own && !staff) {
|
|
107
|
+
const allowed = await memberHasOrgPermission(orgId, member, 'billing.view') || await memberHasOrgPermission(orgId, member, 'org.auditLog');
|
|
108
|
+
if (!allowed) return json({
|
|
109
|
+
error: 'billing.view required'
|
|
110
|
+
}, 403);
|
|
111
|
+
}
|
|
112
|
+
const limit = Math.min(MAX_USER_MONTHS, Math.max(1, Math.floor(Number((_params_limit = params['limit']) != null ? _params_limit : 2) || 2)));
|
|
113
|
+
const rows = await readUserAiUsageMonths(firestore, orgId, subjectUid, limit);
|
|
114
|
+
const orgRef = firestore.collection('orgs').doc(orgId);
|
|
115
|
+
const rollups = rows.length ? await firestore.getAll(...rows.map((row)=>orgRef.collection('assistUsage').doc(row.month))) : [];
|
|
116
|
+
const wire = {
|
|
117
|
+
uid: subjectUid,
|
|
118
|
+
months: rows.map((row, index)=>{
|
|
119
|
+
var _ref;
|
|
120
|
+
var _rollups_index;
|
|
121
|
+
return {
|
|
122
|
+
month: row.month,
|
|
123
|
+
credits: row.credits,
|
|
124
|
+
share: aiUsageShare(row.estCostUsd, Number((_ref = (_rollups_index = rollups[index]) == null ? void 0 : _rollups_index.get('estCostUsd')) != null ? _ref : 0)),
|
|
125
|
+
requests: row.requests,
|
|
126
|
+
refusals: row.refusals,
|
|
127
|
+
byKind: row.byKind,
|
|
128
|
+
byHost: row.byHost
|
|
129
|
+
};
|
|
130
|
+
})
|
|
131
|
+
};
|
|
132
|
+
return json(wire, 200);
|
|
133
|
+
}
|
|
134
|
+
// The table, which is about everyone: the Usage page's permission.
|
|
135
|
+
if (!staff && !await memberHasOrgPermission(orgId, member, 'billing.view')) {
|
|
136
|
+
return json({
|
|
137
|
+
error: 'billing.view required'
|
|
138
|
+
}, 403);
|
|
139
|
+
}
|
|
140
|
+
const usage = await readOrgAiUsageByUser(firestore, orgId, month);
|
|
141
|
+
const rows = usage.rows.map((row)=>toAiUsageWireRow(row, hostId));
|
|
142
|
+
if (hostId) {
|
|
143
|
+
rows.sort((a, b)=>{
|
|
144
|
+
var _b_hostCredits, _a_hostCredits;
|
|
145
|
+
return ((_b_hostCredits = b.hostCredits) != null ? _b_hostCredits : 0) - ((_a_hostCredits = a.hostCredits) != null ? _a_hostCredits : 0) || b.credits - a.credits;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
if (format !== 'csv') {
|
|
149
|
+
const table = {
|
|
150
|
+
month,
|
|
151
|
+
months,
|
|
152
|
+
orgCredits: usage.orgCredits,
|
|
153
|
+
rows
|
|
154
|
+
};
|
|
155
|
+
return json(table, 200);
|
|
156
|
+
}
|
|
157
|
+
/*
|
|
158
|
+
* The file, streamed: the header first, then the rows in pages, so a
|
|
159
|
+
* roster of any size arrives without being held whole. The row count
|
|
160
|
+
* travels in a header the client checks the body against — a stream
|
|
161
|
+
* that dies halfway yields a well-formed shorter file, and nothing about
|
|
162
|
+
* the bytes says it is short.
|
|
163
|
+
*/ const encoder = new TextEncoder();
|
|
164
|
+
const PAGE = 200;
|
|
165
|
+
let cursor = 0;
|
|
166
|
+
let headed = false;
|
|
167
|
+
const stream = new ReadableStream({
|
|
168
|
+
pull (controller) {
|
|
169
|
+
if (!headed) {
|
|
170
|
+
headed = true;
|
|
171
|
+
controller.enqueue(encoder.encode(AI_USAGE_CSV_HEADER.map(csvCell).join(',')));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (cursor < rows.length) {
|
|
175
|
+
const page = rows.slice(cursor, cursor + PAGE);
|
|
176
|
+
cursor += page.length;
|
|
177
|
+
controller.enqueue(encoder.encode(`\n${page.map(aiUsageCsvLine).join('\n')}`));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
controller.enqueue(encoder.encode('\n'));
|
|
181
|
+
controller.close();
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
// Ids and counts only, never content: a report about people leaving
|
|
185
|
+
// the platform is worth a row; who was in it is the workspace's own.
|
|
186
|
+
void firestore.collection('adminAudit').add({
|
|
187
|
+
actorUid: decoded.uid,
|
|
188
|
+
action: 'ai-usage.exported',
|
|
189
|
+
target: `orgs/${orgId}/aiUsageByUser`,
|
|
190
|
+
before: null,
|
|
191
|
+
after: {
|
|
192
|
+
month,
|
|
193
|
+
rows: rows.length,
|
|
194
|
+
hostId: hostId || null
|
|
195
|
+
},
|
|
196
|
+
at: FieldValue.serverTimestamp()
|
|
197
|
+
}).catch(()=>undefined);
|
|
198
|
+
return new Response(stream, {
|
|
199
|
+
status: 200,
|
|
200
|
+
headers: {
|
|
201
|
+
'Content-Type': 'text/csv; charset=utf-8',
|
|
202
|
+
'Content-Disposition': `attachment; filename="ai-usage-${month}.csv"`,
|
|
203
|
+
[AI_USAGE_EXPORT_ROWS_HEADER]: String(rows.length),
|
|
204
|
+
'Cache-Control': 'no-store, private'
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
} catch (error) {
|
|
208
|
+
// A refused credential is a 401, not a fault of ours (AGL-1993). Null
|
|
209
|
+
// for anything else, so a real failure keeps the answer below.
|
|
210
|
+
const unauthenticated = invalidIdTokenResponse(error);
|
|
211
|
+
if (unauthenticated) return unauthenticated;
|
|
212
|
+
console.error('[ai/usage] read failed', orgId, error);
|
|
213
|
+
return json({
|
|
214
|
+
error: 'AI usage unavailable'
|
|
215
|
+
}, 500);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
export { handler as GET };
|
|
219
|
+
|
|
220
|
+
//# sourceMappingURL=ai-usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/server/ai-usage.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 { csvCell, pluginRequestFromWeb } from '@aglyn/aglyn/server'\nimport {\n emailUnverifiedResponse,\n firebaseAdmin,\n isImpersonationSession,\n memberHasOrgPermission,\n resolveOrgMembership,\n} from '@aglyn/tenant-data-admin'\nimport {\n aiUsageMonthKeys,\n aiUsageMonthWithinRetention,\n aiUsageShare,\n type AiUsageByUserMonth,\n} from '../model/ai-usage-by-user'\nimport {\n readOrgAiUsageByUser,\n readUserAiUsageMonths,\n} from '../usage/ai-usage-by-user'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { invalidIdTokenResponse } from '@aglyn/tenant-data-admin/server/id-token-refusal'\nimport {\n AI_USAGE_CSV_HEADER,\n AI_USAGE_EXPORT_ROWS_HEADER,\n aiUsageCsvLine,\n toAiUsageWireRow,\n type OrgAiUsageTableWire,\n type UserAiUsageWire,\n} from '../usage/ai-usage-wire'\n\n// lockdown-423: exempt — a READ-ONLY usage report that writes nothing to the\n// workspace, the posture of ai/billing/credits beside it: a billing-locked\n// owner working out who spent the credits needs this table more, not less.\n\n/**\n * WHO IS GENERATING WHAT, PER SITE (AGL-2928) — the customer's read of the\n * per-user AI usage rollup.\n *\n * ## Three questions, one route\n *\n * - **The month's table** (`orgId`, `month`): every current member with a\n * month document, dearest first, with each person's share of the\n * workspace's spend. Gated on `billing.view`, the Usage page's gate, and\n * served as JSON or — `format=csv` — as a streamed file.\n * - **One person** (`uid`): that person's months, newest first, for the\n * member detail card. The subject reads their own; anyone else needs\n * `billing.view` or `org.auditLog`, which is the team page's gate and the\n * pair the security rules admit for the same documents.\n * - **One site** (`hostId`): the table with each row's credits ON THAT SITE\n * beside it, for the site's collaborators card — the agency question,\n * \"which client site's collaborators spend\".\n *\n * ## No dollar figure crosses this boundary\n *\n * The month document carries `estCostUsd`, our provider bill. The rows here\n * carry credits and a SHARE, and the share is computed on the dollars\n * server-side (`aiUsageShare`) so it is exact — a roster's credits each round\n * up, the org's round once — without the dollars ever being serialized.\n *\n * ## The month bound\n *\n * `month` must be inside the retention window (`aiUsageMonthKeys`): a\n * request for an older month is refused rather than answered empty, because\n * \"nothing for that month\" and \"that month has been reaped\" must not read\n * the same on a report somebody exports.\n */\n\n/** The most months one person's card is handed. */\nconst MAX_USER_MONTHS = 13\n\nconst json = (body: unknown, status: number) => Response.json(body, { status })\n\nasync function handler(request: Request): Promise<Response> {\n const { method, query, headers: rawHeaders } = await pluginRequestFromWeb(request)\n const headers = rawHeaders as Partial<Record<string, string>>\n if (method !== 'GET') return json({ error: 'Method not allowed' }, 405)\n\n const authorization = headers.authorization ?? ''\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) return json({ error: 'Unauthenticated' }, 401)\n\n const params = (query ?? {}) as Record<string, unknown>\n const orgId = String(params['orgId'] ?? '').trim()\n if (!orgId) return json({ error: 'Missing orgId' }, 400)\n const now = new Date()\n const months = aiUsageMonthKeys(now)\n const month = String(params['month'] ?? months[0]).trim()\n if (!aiUsageMonthWithinRetention(month, now)) {\n return json(\n {\n error:\n `AI usage is kept for ${months.length} months; ` +\n `ask for one of ${months[months.length - 1]} through ${months[0]}.`,\n },\n 400,\n )\n }\n const subjectUid = String(params['uid'] ?? '').trim()\n const hostId = String(params['hostId'] ?? '').trim()\n const format = String(params['format'] ?? 'json').trim()\n\n try {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n if (!decoded.email_verified && !isImpersonationSession(decoded)) {\n return emailUnverifiedResponse()\n }\n const staff = decoded['staff'] === true\n const membership = await resolveOrgMembership(decoded.uid, orgId)\n const member = membership?.member\n if (!member && !staff) return json({ error: 'Not found' }, 404)\n\n const firestore = firebaseAdmin.app().firestore()\n\n if (subjectUid) {\n // One person. Their own months are theirs to read; anyone else's need\n // the Usage page's permission or the team page's.\n const own = subjectUid === decoded.uid && Boolean(member)\n if (!own && !staff) {\n const allowed =\n (await memberHasOrgPermission(orgId, member, 'billing.view')) ||\n (await memberHasOrgPermission(orgId, member, 'org.auditLog'))\n if (!allowed) return json({ error: 'billing.view required' }, 403)\n }\n const limit = Math.min(\n MAX_USER_MONTHS,\n Math.max(1, Math.floor(Number(params['limit'] ?? 2) || 2)),\n )\n const rows = await readUserAiUsageMonths(firestore, orgId, subjectUid, limit)\n const orgRef = firestore.collection('orgs').doc(orgId)\n const rollups = rows.length\n ? await firestore.getAll(\n ...rows.map((row) => orgRef.collection('assistUsage').doc(row.month)),\n )\n : []\n const wire: UserAiUsageWire = {\n uid: subjectUid,\n months: rows.map((row: AiUsageByUserMonth, index) => ({\n month: row.month,\n credits: row.credits,\n share: aiUsageShare(\n row.estCostUsd,\n Number(rollups[index]?.get('estCostUsd') ?? 0),\n ),\n requests: row.requests,\n refusals: row.refusals,\n byKind: row.byKind,\n byHost: row.byHost,\n })),\n }\n return json(wire, 200)\n }\n\n // The table, which is about everyone: the Usage page's permission.\n if (!staff && !(await memberHasOrgPermission(orgId, member, 'billing.view'))) {\n return json({ error: 'billing.view required' }, 403)\n }\n const usage = await readOrgAiUsageByUser(firestore, orgId, month)\n const rows = usage.rows.map((row) => toAiUsageWireRow(row, hostId))\n if (hostId) {\n rows.sort(\n (a, b) =>\n (b.hostCredits ?? 0) - (a.hostCredits ?? 0) || b.credits - a.credits,\n )\n }\n\n if (format !== 'csv') {\n const table: OrgAiUsageTableWire = {\n month,\n months,\n orgCredits: usage.orgCredits,\n rows,\n }\n return json(table, 200)\n }\n\n /*\n * The file, streamed: the header first, then the rows in pages, so a\n * roster of any size arrives without being held whole. The row count\n * travels in a header the client checks the body against — a stream\n * that dies halfway yields a well-formed shorter file, and nothing about\n * the bytes says it is short.\n */\n const encoder = new TextEncoder()\n const PAGE = 200\n let cursor = 0\n let headed = false\n const stream = new ReadableStream<Uint8Array>({\n pull(controller) {\n if (!headed) {\n headed = true\n controller.enqueue(encoder.encode(AI_USAGE_CSV_HEADER.map(csvCell).join(',')))\n return\n }\n if (cursor < rows.length) {\n const page = rows.slice(cursor, cursor + PAGE)\n cursor += page.length\n controller.enqueue(\n encoder.encode(`\\n${page.map(aiUsageCsvLine).join('\\n')}`),\n )\n return\n }\n controller.enqueue(encoder.encode('\\n'))\n controller.close()\n },\n })\n\n // Ids and counts only, never content: a report about people leaving\n // the platform is worth a row; who was in it is the workspace's own.\n void firestore\n .collection('adminAudit')\n .add({\n actorUid: decoded.uid,\n action: 'ai-usage.exported',\n target: `orgs/${orgId}/aiUsageByUser`,\n before: null,\n after: { month, rows: rows.length, hostId: hostId || null },\n at: FieldValue.serverTimestamp(),\n })\n .catch(() => undefined)\n\n return new Response(stream, {\n status: 200,\n headers: {\n 'Content-Type': 'text/csv; charset=utf-8',\n 'Content-Disposition': `attachment; filename=\"ai-usage-${month}.csv\"`,\n [AI_USAGE_EXPORT_ROWS_HEADER]: String(rows.length),\n 'Cache-Control': 'no-store, private',\n },\n })\n } catch (error) {\n // A refused credential is a 401, not a fault of ours (AGL-1993). Null\n // for anything else, so a real failure keeps the answer below.\n const unauthenticated = invalidIdTokenResponse(error)\n if (unauthenticated) return unauthenticated\n console.error('[ai/usage] read failed', orgId, error)\n return json({ error: 'AI usage unavailable' }, 500)\n }\n}\n\nexport { handler as GET }\n"],"names":["csvCell","pluginRequestFromWeb","emailUnverifiedResponse","firebaseAdmin","isImpersonationSession","memberHasOrgPermission","resolveOrgMembership","aiUsageMonthKeys","aiUsageMonthWithinRetention","aiUsageShare","readOrgAiUsageByUser","readUserAiUsageMonths","FieldValue","invalidIdTokenResponse","AI_USAGE_CSV_HEADER","AI_USAGE_EXPORT_ROWS_HEADER","aiUsageCsvLine","toAiUsageWireRow","MAX_USER_MONTHS","json","body","status","Response","handler","request","headers","params","method","query","rawHeaders","error","authorization","idToken","startsWith","slice","length","undefined","orgId","String","trim","now","Date","months","month","subjectUid","hostId","format","decoded","app","auth","verifyIdToken","email_verified","staff","membership","uid","member","firestore","own","Boolean","allowed","limit","Math","min","max","floor","Number","rows","orgRef","collection","doc","rollups","getAll","map","row","wire","index","credits","share","estCostUsd","get","requests","refusals","byKind","byHost","usage","sort","a","b","hostCredits","table","orgCredits","encoder","TextEncoder","PAGE","cursor","headed","stream","ReadableStream","pull","controller","enqueue","encode","join","page","close","add","actorUid","action","target","before","after","at","serverTimestamp","catch","unauthenticated","console","GET"],"mappings":"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"}
|