@aglyn/plugins-ai 1.0.0-beta.270 → 1.0.0-beta.272
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/package.json +17 -17
- package/src/lib/activity/ai-activity-actions.d.ts +4 -3
- package/src/lib/activity/ai-activity-actions.js +4 -0
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/assets/site-styles/beauty.webp +0 -0
- package/src/lib/assets/site-styles/blog.webp +0 -0
- package/src/lib/assets/site-styles/business.webp +0 -0
- package/src/lib/assets/site-styles/coming-soon.webp +0 -0
- package/src/lib/assets/site-styles/education.webp +0 -0
- package/src/lib/assets/site-styles/events.webp +0 -0
- package/src/lib/assets/site-styles/fitness.webp +0 -0
- package/src/lib/assets/site-styles/landing.webp +0 -0
- package/src/lib/assets/site-styles/music.webp +0 -0
- package/src/lib/assets/site-styles/nonprofit.webp +0 -0
- package/src/lib/assets/site-styles/personal.webp +0 -0
- package/src/lib/assets/site-styles/photography.webp +0 -0
- package/src/lib/assets/site-styles/portfolio.webp +0 -0
- package/src/lib/assets/site-styles/professional.webp +0 -0
- package/src/lib/assets/site-styles/realestate.webp +0 -0
- package/src/lib/assets/site-styles/restaurant.webp +0 -0
- package/src/lib/assets/site-styles/store.webp +0 -0
- package/src/lib/assets/site-styles/studio.webp +0 -0
- package/src/lib/assets/site-styles/trades.webp +0 -0
- package/src/lib/assets/site-styles/wellness.webp +0 -0
- package/src/lib/assets/site-styles/yoga.webp +0 -0
- package/src/lib/components/ai-credits-card.component.d.ts +4 -3
- package/src/lib/components/ai-credits-card.component.js +15 -15
- package/src/lib/components/ai-credits-card.component.js.map +1 -1
- package/src/lib/components/ai-job-input-form.component.d.ts +36 -0
- package/src/lib/components/ai-job-input-form.component.js +100 -0
- package/src/lib/components/ai-job-input-form.component.js.map +1 -0
- package/src/lib/components/ai-job-requests.d.ts +4 -0
- package/src/lib/components/ai-job-requests.js +4 -0
- package/src/lib/components/ai-job-requests.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +18 -0
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-import-job.component.d.ts +41 -0
- package/src/lib/components/ai-site-import-job.component.js +707 -0
- package/src/lib/components/ai-site-import-job.component.js.map +1 -0
- package/src/lib/components/ai-site-import-start.component.d.ts +64 -0
- package/src/lib/components/ai-site-import-start.component.js +335 -0
- package/src/lib/components/ai-site-import-start.component.js.map +1 -0
- package/src/lib/components/ai-site-ownership-proof.component.d.ts +42 -0
- package/src/lib/components/ai-site-ownership-proof.component.js +353 -0
- package/src/lib/components/ai-site-ownership-proof.component.js.map +1 -0
- package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
- package/src/lib/components/ai-site-start-card.component.js +261 -119
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-site-start-steps.component.d.ts +43 -2
- package/src/lib/components/ai-site-start-steps.component.js +651 -15
- package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
- package/src/lib/components/ai-site-start-tracking.d.ts +10 -0
- package/src/lib/components/ai-site-start-tracking.js +14 -0
- package/src/lib/components/ai-site-start-tracking.js.map +1 -1
- package/src/lib/components/ai-site-style-previews.js +26 -22
- package/src/lib/components/ai-site-style-previews.js.map +1 -1
- package/src/lib/components/ai-upsell-dialog.component.d.ts +7 -5
- package/src/lib/components/ai-upsell-dialog.component.js +8 -6
- package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
- package/src/lib/components/assist-build-card.component.js +11 -1
- package/src/lib/components/assist-build-card.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +9 -1
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +8 -4
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-card.component.js +4 -7
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
- package/src/lib/declarations.d.ts +2 -2
- package/src/lib/declarations.js +9 -5
- package/src/lib/declarations.js.map +1 -1
- package/src/lib/jobs/ai-build-capabilities.js +3 -1
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.d.ts +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.js +14 -3
- package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +5 -3
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.d.ts +14 -0
- package/src/lib/jobs/ai-job-drafts.js +24 -0
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-form-step.d.ts +15 -1
- package/src/lib/jobs/ai-job-form-step.js +75 -8
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-import-step.d.ts +136 -0
- package/src/lib/jobs/ai-job-import-step.js +809 -0
- package/src/lib/jobs/ai-job-import-step.js.map +1 -0
- package/src/lib/jobs/ai-job-page-language.d.ts +52 -1
- package/src/lib/jobs/ai-job-page-language.js +99 -5
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +19 -2
- package/src/lib/jobs/ai-job-page-step.js +42 -18
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +25 -2
- package/src/lib/jobs/ai-job-plan-step.js +103 -29
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-settings-step.js +4 -3
- package/src/lib/jobs/ai-job-settings-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +27 -0
- package/src/lib/jobs/ai-job-site-datasets.js +140 -14
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-job-site-look.js +18 -5
- package/src/lib/jobs/ai-job-site-look.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +16 -1
- package/src/lib/jobs/ai-job-site-step.js +95 -22
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +15 -1
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-job-tracking-step.d.ts +14 -3
- package/src/lib/jobs/ai-job-tracking-step.js +28 -5
- package/src/lib/jobs/ai-job-tracking-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +13 -1
- package/src/lib/jobs/ai-jobs.js +77 -10
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-owner-photos.d.ts +16 -1
- package/src/lib/jobs/ai-layout-owner-photos.js +47 -0
- package/src/lib/jobs/ai-layout-owner-photos.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +27 -2
- package/src/lib/jobs/ai-layout-stock-photos.js +190 -19
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-inspiration-signals.d.ts +98 -0
- package/src/lib/jobs/ai-site-inspiration-signals.js +585 -0
- package/src/lib/jobs/ai-site-inspiration-signals.js.map +1 -0
- package/src/lib/jobs/ai-site-inspiration.d.ts +157 -0
- package/src/lib/jobs/ai-site-inspiration.js +346 -0
- package/src/lib/jobs/ai-site-inspiration.js.map +1 -0
- package/src/lib/jobs/ai-site-publish.d.ts +26 -1
- package/src/lib/jobs/ai-site-publish.js +162 -13
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/ai-theme-brand-inputs.d.ts +77 -3
- package/src/lib/jobs/ai-theme-brand-inputs.js +135 -31
- package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.d.ts +97 -0
- package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.js +137 -0
- package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-site-inspiration-pages.d.ts +38 -0
- package/src/lib/jobs/fixtures/ai-site-inspiration-pages.js +103 -0
- package/src/lib/jobs/fixtures/ai-site-inspiration-pages.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.d.ts +25 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.js +174 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +1 -1
- package/src/lib/layout-language/ai-layout-compiler.js +438 -25
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-contact.d.ts +57 -0
- package/src/lib/layout-language/ai-layout-contact.js +137 -0
- package/src/lib/layout-language/ai-layout-contact.js.map +1 -0
- package/src/lib/layout-language/ai-layout-copy.d.ts +12 -0
- package/src/lib/layout-language/ai-layout-copy.js +34 -2
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -1
- package/src/lib/layout-language/ai-layout-design.d.ts +65 -8
- package/src/lib/layout-language/ai-layout-design.js +411 -97
- package/src/lib/layout-language/ai-layout-design.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.js +23 -0
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-labels.d.ts +2 -0
- package/src/lib/layout-language/ai-layout-labels.js +12 -6
- package/src/lib/layout-language/ai-layout-labels.js.map +1 -1
- package/src/lib/layout-language/ai-layout-language.d.ts +3 -2
- package/src/lib/layout-language/ai-layout-language.js +4 -3
- package/src/lib/layout-language/ai-layout-language.js.map +1 -1
- package/src/lib/layout-language/ai-layout-links.d.ts +13 -0
- package/src/lib/layout-language/ai-layout-links.js +30 -0
- package/src/lib/layout-language/ai-layout-links.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +49 -6
- package/src/lib/layout-language/ai-layout-pictures.js +145 -22
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/model/ai-build-job.d.ts +16 -1
- package/src/lib/model/ai-build-job.js +40 -0
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +20 -0
- package/src/lib/model/ai-credit-estimate.js +13 -0
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-job-activity.d.ts +2 -2
- package/src/lib/model/ai-job-activity.js +6 -4
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-job-input.d.ts +33 -0
- package/src/lib/model/ai-job-input.js +59 -0
- package/src/lib/model/ai-job-input.js.map +1 -0
- package/src/lib/model/ai-job-notice.d.ts +4 -0
- package/src/lib/model/ai-job-notice.js +28 -4
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +69 -3
- package/src/lib/model/ai-jobs.types.js +2 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-site-details.d.ts +122 -0
- package/src/lib/model/ai-site-details.js +300 -0
- package/src/lib/model/ai-site-details.js.map +1 -0
- package/src/lib/model/ai-site-entity.d.ts +181 -0
- package/src/lib/model/ai-site-entity.js +390 -0
- package/src/lib/model/ai-site-entity.js.map +1 -0
- package/src/lib/model/ai-site-import-view.d.ts +130 -0
- package/src/lib/model/ai-site-import-view.js +182 -0
- package/src/lib/model/ai-site-import-view.js.map +1 -0
- package/src/lib/model/ai-site-import.d.ts +302 -0
- package/src/lib/model/ai-site-import.js +526 -0
- package/src/lib/model/ai-site-import.js.map +1 -0
- package/src/lib/model/ai-site-inspiration.d.ts +72 -0
- package/src/lib/model/ai-site-inspiration.js +109 -0
- package/src/lib/model/ai-site-inspiration.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +13 -1
- package/src/lib/model/ai-site-job.js +15 -2
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.js +109 -21
- package/src/lib/model/ai-site-kinds.js.map +1 -1
- package/src/lib/model/ai-site-look.d.ts +10 -0
- package/src/lib/model/ai-site-look.js +24 -0
- package/src/lib/model/ai-site-look.js.map +1 -1
- package/src/lib/model/ai-site-start-seo.d.ts +19 -5
- package/src/lib/model/ai-site-start-seo.js +25 -7
- package/src/lib/model/ai-site-start-seo.js.map +1 -1
- package/src/lib/model/ai-site-start-steps.d.ts +9 -3
- package/src/lib/model/ai-site-start-steps.js +67 -5
- package/src/lib/model/ai-site-start-steps.js.map +1 -1
- package/src/lib/model/ai-site-start.d.ts +81 -4
- package/src/lib/model/ai-site-start.js +207 -13
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/ai-upsell-copy.js +4 -1
- package/src/lib/model/ai-upsell-copy.js.map +1 -1
- package/src/lib/plan-entitlements.d.ts +4 -3
- package/src/lib/plan-entitlements.js +3 -8
- package/src/lib/plan-entitlements.js.map +1 -1
- package/src/lib/runtime/ai-dataset-generation.d.ts +8 -1
- package/src/lib/runtime/ai-dataset-generation.js +12 -2
- package/src/lib/runtime/ai-dataset-generation.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +26 -33
- package/src/lib/runtime/ai-doctrine-validators.js +54 -44
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +1 -1
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.d.ts +7 -0
- package/src/lib/runtime/ai-node-tree.js +9 -1
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/server/ai-create-route.d.ts +1 -1
- package/src/lib/server/ai-create-route.js +3 -4
- package/src/lib/server/ai-create-route.js.map +1 -1
- package/src/lib/server/ai-jobs-resume.js +27 -3
- package/src/lib/server/ai-jobs-resume.js.map +1 -1
- package/src/lib/server/ai-jobs-route.js +18 -0
- package/src/lib/server/ai-jobs-route.js.map +1 -1
- package/src/lib/server/ai-site-import-route.d.ts +89 -0
- package/src/lib/server/ai-site-import-route.js +356 -0
- package/src/lib/server/ai-site-import-route.js.map +1 -0
- package/src/lib/server.js +14 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/site-import/fixtures/site-import-crawl-recordings.d.ts +45 -0
- package/src/lib/site-import/fixtures/site-import-crawl-recordings.js +313 -0
- package/src/lib/site-import/fixtures/site-import-crawl-recordings.js.map +1 -0
- package/src/lib/site-import/fixtures/site-import-wave-recordings.d.ts +50 -0
- package/src/lib/site-import/fixtures/site-import-wave-recordings.js +217 -0
- package/src/lib/site-import/fixtures/site-import-wave-recordings.js.map +1 -0
- package/src/lib/site-import/site-import-crawl.d.ts +163 -0
- package/src/lib/site-import/site-import-crawl.js +467 -0
- package/src/lib/site-import/site-import-crawl.js.map +1 -0
- package/src/lib/site-import/site-import-flag.d.ts +23 -0
- package/src/lib/site-import/site-import-flag.js +23 -0
- package/src/lib/site-import/site-import-flag.js.map +1 -0
- package/src/lib/site-import/site-import-html.d.ts +94 -0
- package/src/lib/site-import/site-import-html.js +462 -0
- package/src/lib/site-import/site-import-html.js.map +1 -0
- package/src/lib/site-import/site-import-images.d.ts +54 -0
- package/src/lib/site-import/site-import-images.js +204 -0
- package/src/lib/site-import/site-import-images.js.map +1 -0
- package/src/lib/site-import/site-import-page.d.ts +48 -0
- package/src/lib/site-import/site-import-page.js +150 -0
- package/src/lib/site-import/site-import-page.js.map +1 -0
- package/src/lib/site-import/site-import-review.d.ts +40 -0
- package/src/lib/site-import/site-import-review.js +149 -0
- package/src/lib/site-import/site-import-review.js.map +1 -0
- package/src/lib/site-import/site-import-robots.d.ts +52 -0
- package/src/lib/site-import/site-import-robots.js +126 -0
- package/src/lib/site-import/site-import-robots.js.map +1 -0
- package/src/lib/usage/assist-credits.d.ts +4 -4
- package/src/lib/usage/assist-credits.js +4 -4
- package/src/lib/usage/assist-credits.js.map +1 -1
- package/src/lib/usage/staff-org-ai-view.js +6 -3
- package/src/lib/usage/staff-org-ai-view.js.map +1 -1
- package/src/lib/usage/staff-org-ai.d.ts +4 -2
- package/src/lib/usage/staff-org-ai.js +7 -7
- package/src/lib/usage/staff-org-ai.js.map +1 -1
- package/src/lib/usage/staff-org-usage-ai.js +2 -2
- package/src/lib/usage/staff-org-usage-ai.js.map +1 -1
|
@@ -0,0 +1,356 @@
|
|
|
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
|
+
* The "Bring my site" doors (AGL-3974, parent AGL-3973).
|
|
18
|
+
*
|
|
19
|
+
* GET /api/ai/site-import/ownership?orgId= the org's claims
|
|
20
|
+
* POST /api/ai/site-import/ownership { orgId, action, domain, … }
|
|
21
|
+
* action `claim` start a claim (idempotent)
|
|
22
|
+
* action `check`, method `dns` | `meta` look for the record or tag now
|
|
23
|
+
* action `send-code`, email email a code to an address at the domain
|
|
24
|
+
* action `confirm-code`, code check the code
|
|
25
|
+
* POST /api/ai/site-import/crawl { orgId, domain, maxPages? }
|
|
26
|
+
* the site's inventory, for a domain the org has a CURRENT proof for
|
|
27
|
+
*
|
|
28
|
+
* The rungs, in order, each disclosing nothing a lower one would:
|
|
29
|
+
*
|
|
30
|
+
* 401 no bearer token, or a refused one
|
|
31
|
+
* 403 email unverified (an impersonation session is exempt)
|
|
32
|
+
* 400 the request did not name the org (AGL-1934)
|
|
33
|
+
* 403 not a member of that org
|
|
34
|
+
* 404 `release_ai_site_import` is off; a verified staff claim previews
|
|
35
|
+
* 403 `reason: 'role'`: the caller is not the org's owner or an admin
|
|
36
|
+
* 403 `reason: 'plan'`: the org is on the Free plan; import is for every
|
|
37
|
+
* paid plan
|
|
38
|
+
* 423 lockdown
|
|
39
|
+
*
|
|
40
|
+
* Nothing here spends model credits: the crawl reads and returns, and the
|
|
41
|
+
* import job (another lane) decides what becomes a page.
|
|
42
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
43
|
+
import { resolveEffectivePlan, resolveOrgEntitlements } from "@aglyn/aglyn/app-utils/plan-entitlements";
|
|
44
|
+
import { addAdminAudit } from "@aglyn/tenant-data-admin/server/admin-audit-write";
|
|
45
|
+
import { checkDomainOwnership, confirmDomainOwnershipCode, DOMAIN_OWNERSHIP_META_NAME, isDomainOwnershipProven, issueDomainOwnershipClaim, listDomainOwnershipClaims, normalizeOwnershipDomain, sendDomainOwnershipCode } from "@aglyn/tenant-data-admin/server/domain-ownership";
|
|
46
|
+
import { emailUnverifiedResponse, firebaseAdmin, isImpersonationSession } from "@aglyn/tenant-data-admin/server/firebase-admin";
|
|
47
|
+
import { invalidIdTokenResponse } from "@aglyn/tenant-data-admin/server/id-token-refusal";
|
|
48
|
+
import { lockdownRefusal } from "@aglyn/tenant-data-admin/server/lockdown";
|
|
49
|
+
import { getOrgForUser } from "@aglyn/tenant-data-admin/server/organizations";
|
|
50
|
+
import { consumeRateLimit } from "@aglyn/tenant-data-admin/server/rate-limit-store";
|
|
51
|
+
import { isServerReleaseFlagOnForOrg } from "@aglyn/tenant-data-admin/server/release-flags";
|
|
52
|
+
import { FieldValue } from "firebase-admin/firestore";
|
|
53
|
+
import { crawlSiteForImport, readSiteVerificationMeta } from "../site-import/site-import-crawl.js";
|
|
54
|
+
import { SITE_IMPORT_RELEASE_FLAG } from "../site-import/site-import-flag.js";
|
|
55
|
+
export { SITE_IMPORT_RELEASE_FLAG };
|
|
56
|
+
/** Pages the crawl door reads in one request; the import job reads in waves. */ export const SITE_IMPORT_DOOR_MAX_PAGES = 25;
|
|
57
|
+
/** Crawls one org may run through the door in a day. */ export const SITE_IMPORT_CRAWLS_PER_ORG_PER_DAY = 20;
|
|
58
|
+
/** DNS and home-page checks one org may run in an hour. */ export const SITE_IMPORT_CHECKS_PER_ORG_PER_HOUR = 60;
|
|
59
|
+
/** The door's own clock for a crawl, inside the route's `maxDuration`. */ const SITE_IMPORT_DOOR_TIME_MS = 45000;
|
|
60
|
+
function bearerToken(request) {
|
|
61
|
+
var _request_headers_get;
|
|
62
|
+
const authorization = (_request_headers_get = request.headers.get('authorization')) != null ? _request_headers_get : '';
|
|
63
|
+
return authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
|
|
64
|
+
}
|
|
65
|
+
export async function siteImportGate(request, rawOrgId) {
|
|
66
|
+
var _resolved_member, _resolved_org;
|
|
67
|
+
const idToken = bearerToken(request);
|
|
68
|
+
if (!idToken) return Response.json({
|
|
69
|
+
error: 'Unauthenticated'
|
|
70
|
+
}, {
|
|
71
|
+
status: 401
|
|
72
|
+
});
|
|
73
|
+
const app = firebaseAdmin.app();
|
|
74
|
+
let decoded;
|
|
75
|
+
try {
|
|
76
|
+
decoded = await app.auth().verifyIdToken(idToken);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
const unauthenticated = invalidIdTokenResponse(error);
|
|
79
|
+
if (unauthenticated) return unauthenticated;
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
if (!decoded.email_verified && !isImpersonationSession(decoded)) return emailUnverifiedResponse();
|
|
83
|
+
const staff = decoded['staff'] === true;
|
|
84
|
+
const orgId = String(rawOrgId != null ? rawOrgId : '').trim();
|
|
85
|
+
if (!orgId) {
|
|
86
|
+
return Response.json({
|
|
87
|
+
error: 'Open a workspace before using this feature'
|
|
88
|
+
}, {
|
|
89
|
+
status: 400
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const resolved = await getOrgForUser(decoded.uid, orgId);
|
|
93
|
+
if (!resolved || resolved.orgId !== orgId) {
|
|
94
|
+
return Response.json({
|
|
95
|
+
error: 'You are not a member of that organization'
|
|
96
|
+
}, {
|
|
97
|
+
status: 403
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (!staff && !await isServerReleaseFlagOnForOrg(SITE_IMPORT_RELEASE_FLAG, orgId)) {
|
|
101
|
+
return Response.json({
|
|
102
|
+
error: 'Not found'
|
|
103
|
+
}, {
|
|
104
|
+
status: 404
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
const member = (_resolved_member = resolved.member) != null ? _resolved_member : null;
|
|
108
|
+
if (!member || member.role !== 'owner' && member.role !== 'admin') {
|
|
109
|
+
return Response.json({
|
|
110
|
+
error: 'Only the workspace owner or an admin can bring a site in.',
|
|
111
|
+
reason: 'role'
|
|
112
|
+
}, {
|
|
113
|
+
status: 403
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const org = (_resolved_org = resolved.org) != null ? _resolved_org : {};
|
|
117
|
+
if (resolveEffectivePlan(org) === 'free') {
|
|
118
|
+
return Response.json({
|
|
119
|
+
error: 'Bringing an existing site in is included on every paid plan.',
|
|
120
|
+
reason: 'plan'
|
|
121
|
+
}, {
|
|
122
|
+
status: 403
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const locked = await lockdownRefusal({
|
|
126
|
+
request,
|
|
127
|
+
staff,
|
|
128
|
+
uid: decoded.uid,
|
|
129
|
+
org: org
|
|
130
|
+
});
|
|
131
|
+
if (locked) return locked;
|
|
132
|
+
return {
|
|
133
|
+
uid: decoded.uid,
|
|
134
|
+
decoded,
|
|
135
|
+
staff,
|
|
136
|
+
orgId,
|
|
137
|
+
org,
|
|
138
|
+
member,
|
|
139
|
+
firestore: app.firestore()
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Whether "Bring my site" is offered to this reader (AGL-3976), in the
|
|
144
|
+
* doors' own order, for Create site's choice card: `null` while the flag is
|
|
145
|
+
* off (the card is not drawn), `role` or `plan` for a refusal the workspace
|
|
146
|
+
* can change (the card is drawn disabled with the reason), `ready` otherwise.
|
|
147
|
+
* Read by the jobs list only when the card asks, so no other read pays for it.
|
|
148
|
+
*/ export async function siteImportOfferFor(input) {
|
|
149
|
+
var _input_org;
|
|
150
|
+
try {
|
|
151
|
+
if (!input.staff && !await isServerReleaseFlagOnForOrg(SITE_IMPORT_RELEASE_FLAG, input.orgId)) return null;
|
|
152
|
+
} catch (unused) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
if (!input.member || input.member.role !== 'owner' && input.member.role !== 'admin') return 'role';
|
|
156
|
+
if (resolveEffectivePlan((_input_org = input.org) != null ? _input_org : {}) === 'free') return 'plan';
|
|
157
|
+
return 'ready';
|
|
158
|
+
}
|
|
159
|
+
async function audit(gate, action, domain, after) {
|
|
160
|
+
try {
|
|
161
|
+
var _gate_decoded_email;
|
|
162
|
+
await addAdminAudit(gate.firestore, {
|
|
163
|
+
actorUid: gate.uid,
|
|
164
|
+
actorEmail: (_gate_decoded_email = gate.decoded.email) != null ? _gate_decoded_email : null,
|
|
165
|
+
action,
|
|
166
|
+
target: `orgs/${gate.orgId}/domainOwnershipClaims/${domain}`,
|
|
167
|
+
after: _extends({}, after, gate.staff ? {
|
|
168
|
+
staff: true
|
|
169
|
+
} : {}),
|
|
170
|
+
at: FieldValue.serverTimestamp()
|
|
171
|
+
});
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.error('site import audit write failed', {
|
|
174
|
+
orgId: gate.orgId,
|
|
175
|
+
action,
|
|
176
|
+
error
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function ownershipDeps(seams) {
|
|
181
|
+
return _extends({
|
|
182
|
+
readVerificationMeta: (domain)=>readSiteVerificationMeta(domain, DOMAIN_OWNERSHIP_META_NAME, seams.crawl)
|
|
183
|
+
}, seams.ownership);
|
|
184
|
+
}
|
|
185
|
+
const tooMany = (error)=>Response.json({
|
|
186
|
+
error,
|
|
187
|
+
reason: 'rate'
|
|
188
|
+
}, {
|
|
189
|
+
status: 429
|
|
190
|
+
});
|
|
191
|
+
/** `GET /api/ai/site-import/ownership?orgId=`. */ export async function listSiteImportOwnership(request, seams = {}) {
|
|
192
|
+
var _searchParams_get;
|
|
193
|
+
const orgId = (_searchParams_get = new URL(request.url).searchParams.get('orgId')) != null ? _searchParams_get : '';
|
|
194
|
+
const gate = await siteImportGate(request, orgId);
|
|
195
|
+
if (gate instanceof Response) return gate;
|
|
196
|
+
const claims = await listDomainOwnershipClaims(gate.orgId, ownershipDeps(seams));
|
|
197
|
+
return Response.json({
|
|
198
|
+
claims
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
/** `POST /api/ai/site-import/ownership`. */ export async function postSiteImportOwnership(request, seams = {}) {
|
|
202
|
+
var _body_orgId, _body_action, _body_domain;
|
|
203
|
+
const body = await request.json().catch(()=>null);
|
|
204
|
+
if (!body || typeof body !== 'object') {
|
|
205
|
+
return Response.json({
|
|
206
|
+
error: 'Send a JSON body'
|
|
207
|
+
}, {
|
|
208
|
+
status: 400
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
const gate = await siteImportGate(request, String((_body_orgId = body['orgId']) != null ? _body_orgId : ''));
|
|
212
|
+
if (gate instanceof Response) return gate;
|
|
213
|
+
const deps = ownershipDeps(seams);
|
|
214
|
+
const action = String((_body_action = body['action']) != null ? _body_action : '');
|
|
215
|
+
const domain = String((_body_domain = body['domain']) != null ? _body_domain : '');
|
|
216
|
+
if (action === 'claim') {
|
|
217
|
+
const { claim, error } = await issueDomainOwnershipClaim(gate.orgId, domain, gate.uid, deps);
|
|
218
|
+
if (!claim) return Response.json({
|
|
219
|
+
error
|
|
220
|
+
}, {
|
|
221
|
+
status: 400
|
|
222
|
+
});
|
|
223
|
+
await audit(gate, 'site-import.ownership.claim', claim.domain, {
|
|
224
|
+
status: claim.status
|
|
225
|
+
});
|
|
226
|
+
return Response.json({
|
|
227
|
+
claim
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
if (action === 'check') {
|
|
231
|
+
const method = body['method'];
|
|
232
|
+
if (method !== 'dns' && method !== 'meta') {
|
|
233
|
+
return Response.json({
|
|
234
|
+
error: 'method must be dns or meta'
|
|
235
|
+
}, {
|
|
236
|
+
status: 400
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
const window = await consumeRateLimit(`site-import-ownership-check:${gate.orgId}`, {
|
|
240
|
+
limit: SITE_IMPORT_CHECKS_PER_ORG_PER_HOUR,
|
|
241
|
+
windowMs: 60 * 60 * 1000
|
|
242
|
+
});
|
|
243
|
+
if (!window.allowed) return tooMany('Too many checks. Try again in an hour.');
|
|
244
|
+
const { result, error } = await checkDomainOwnership(gate.orgId, domain, method, deps);
|
|
245
|
+
if (!result) return Response.json({
|
|
246
|
+
error
|
|
247
|
+
}, {
|
|
248
|
+
status: 400
|
|
249
|
+
});
|
|
250
|
+
if (result.status === 'proven') {
|
|
251
|
+
await audit(gate, 'site-import.ownership.proven', result.claim.domain, {
|
|
252
|
+
method,
|
|
253
|
+
detail: result.claim.provenDetail
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return Response.json({
|
|
257
|
+
result
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
if (action === 'send-code') {
|
|
261
|
+
var _body_email;
|
|
262
|
+
const sent = await sendDomainOwnershipCode(gate.orgId, domain, String((_body_email = body['email']) != null ? _body_email : ''), {
|
|
263
|
+
org: gate.org
|
|
264
|
+
}, deps);
|
|
265
|
+
if (!sent.claim) return Response.json({
|
|
266
|
+
error: sent.error
|
|
267
|
+
}, {
|
|
268
|
+
status: sent.status
|
|
269
|
+
});
|
|
270
|
+
await audit(gate, 'site-import.ownership.code-sent', sent.claim.domain, {
|
|
271
|
+
address: sent.claim.email.pendingAddress
|
|
272
|
+
});
|
|
273
|
+
return Response.json({
|
|
274
|
+
claim: sent.claim
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
if (action === 'confirm-code') {
|
|
278
|
+
var _body_code;
|
|
279
|
+
const { result, error } = await confirmDomainOwnershipCode(gate.orgId, domain, String((_body_code = body['code']) != null ? _body_code : ''), deps);
|
|
280
|
+
if (!result) return Response.json({
|
|
281
|
+
error
|
|
282
|
+
}, {
|
|
283
|
+
status: 400
|
|
284
|
+
});
|
|
285
|
+
if (result.status === 'proven') {
|
|
286
|
+
await audit(gate, 'site-import.ownership.proven', result.claim.domain, {
|
|
287
|
+
method: 'email',
|
|
288
|
+
detail: result.claim.provenDetail
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
return Response.json({
|
|
292
|
+
result
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
return Response.json({
|
|
296
|
+
error: 'action must be claim, check, send-code or confirm-code'
|
|
297
|
+
}, {
|
|
298
|
+
status: 400
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
/** `POST /api/ai/site-import/crawl`. */ export async function postSiteImportCrawl(request, seams = {}) {
|
|
302
|
+
var _body_orgId, _body_domain;
|
|
303
|
+
const body = await request.json().catch(()=>null);
|
|
304
|
+
if (!body || typeof body !== 'object') {
|
|
305
|
+
return Response.json({
|
|
306
|
+
error: 'Send a JSON body'
|
|
307
|
+
}, {
|
|
308
|
+
status: 400
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
const gate = await siteImportGate(request, String((_body_orgId = body['orgId']) != null ? _body_orgId : ''));
|
|
312
|
+
if (gate instanceof Response) return gate;
|
|
313
|
+
const { domain, error } = normalizeOwnershipDomain(String((_body_domain = body['domain']) != null ? _body_domain : ''));
|
|
314
|
+
if (!domain) return Response.json({
|
|
315
|
+
error
|
|
316
|
+
}, {
|
|
317
|
+
status: 400
|
|
318
|
+
});
|
|
319
|
+
// Ownership BEFORE any page is read: a crawl of a site the org has not
|
|
320
|
+
// proved is exactly the clone this whole feature is built to refuse.
|
|
321
|
+
if (!await isDomainOwnershipProven(gate.orgId, domain, ownershipDeps(seams))) {
|
|
322
|
+
return Response.json({
|
|
323
|
+
error: 'Verify that you own this site first.',
|
|
324
|
+
reason: 'ownership'
|
|
325
|
+
}, {
|
|
326
|
+
status: 403
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
const window = await consumeRateLimit(`site-import-crawl:${gate.orgId}`, {
|
|
330
|
+
limit: SITE_IMPORT_CRAWLS_PER_ORG_PER_DAY,
|
|
331
|
+
windowMs: 24 * 60 * 60 * 1000
|
|
332
|
+
});
|
|
333
|
+
if (!window.allowed) return tooMany('This workspace has read sites enough times today. Try again tomorrow.');
|
|
334
|
+
const planPages = resolveOrgEntitlements(gate.org).screensPerHost;
|
|
335
|
+
const asked = Number(body['maxPages']);
|
|
336
|
+
const maxPages = Math.max(1, Math.min(SITE_IMPORT_DOOR_MAX_PAGES, Number.isFinite(planPages) && planPages > 0 ? planPages : SITE_IMPORT_DOOR_MAX_PAGES, Number.isFinite(asked) && asked > 0 ? Math.floor(asked) : SITE_IMPORT_DOOR_MAX_PAGES));
|
|
337
|
+
const inventory = await crawlSiteForImport({
|
|
338
|
+
domain,
|
|
339
|
+
maxPages,
|
|
340
|
+
timeBudgetMs: SITE_IMPORT_DOOR_TIME_MS,
|
|
341
|
+
signal: request.signal
|
|
342
|
+
}, seams.crawl);
|
|
343
|
+
await audit(gate, 'site-import.crawl', domain, {
|
|
344
|
+
pages: inventory.pages.length,
|
|
345
|
+
skipped: inventory.skipped.length + inventory.skippedOverflow,
|
|
346
|
+
loginPagesRefused: inventory.skipped.filter((entry)=>entry.reason === 'login-form').length,
|
|
347
|
+
stoppedBy: inventory.stoppedBy,
|
|
348
|
+
refused: inventory.refused,
|
|
349
|
+
bytesRead: inventory.bytesRead
|
|
350
|
+
});
|
|
351
|
+
return Response.json({
|
|
352
|
+
inventory
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
//# sourceMappingURL=ai-site-import-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/server/ai-site-import-route.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/**\n * The \"Bring my site\" doors (AGL-3974, parent AGL-3973).\n *\n * GET /api/ai/site-import/ownership?orgId= the org's claims\n * POST /api/ai/site-import/ownership { orgId, action, domain, … }\n * action `claim` start a claim (idempotent)\n * action `check`, method `dns` | `meta` look for the record or tag now\n * action `send-code`, email email a code to an address at the domain\n * action `confirm-code`, code check the code\n * POST /api/ai/site-import/crawl { orgId, domain, maxPages? }\n * the site's inventory, for a domain the org has a CURRENT proof for\n *\n * The rungs, in order, each disclosing nothing a lower one would:\n *\n * 401 no bearer token, or a refused one\n * 403 email unverified (an impersonation session is exempt)\n * 400 the request did not name the org (AGL-1934)\n * 403 not a member of that org\n * 404 `release_ai_site_import` is off; a verified staff claim previews\n * 403 `reason: 'role'`: the caller is not the org's owner or an admin\n * 403 `reason: 'plan'`: the org is on the Free plan; import is for every\n * paid plan\n * 423 lockdown\n *\n * Nothing here spends model credits: the crawl reads and returns, and the\n * import job (another lane) decides what becomes a page.\n */\n\nimport type { DecodedIdToken } from 'firebase-admin/auth'\nimport { resolveEffectivePlan, resolveOrgEntitlements } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport type { AglynOrganization, AglynOrgMember } from '@aglyn/aglyn/foundation/definitions/organization.types'\nimport { addAdminAudit } from '@aglyn/tenant-data-admin/server/admin-audit-write'\nimport {\n checkDomainOwnership,\n confirmDomainOwnershipCode,\n DOMAIN_OWNERSHIP_META_NAME,\n isDomainOwnershipProven,\n issueDomainOwnershipClaim,\n listDomainOwnershipClaims,\n normalizeOwnershipDomain,\n sendDomainOwnershipCode,\n type DomainOwnershipDeps,\n} from '@aglyn/tenant-data-admin/server/domain-ownership'\nimport {\n emailUnverifiedResponse,\n firebaseAdmin,\n isImpersonationSession,\n} from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { invalidIdTokenResponse } from '@aglyn/tenant-data-admin/server/id-token-refusal'\nimport { lockdownRefusal } from '@aglyn/tenant-data-admin/server/lockdown'\nimport { getOrgForUser } from '@aglyn/tenant-data-admin/server/organizations'\nimport { consumeRateLimit } from '@aglyn/tenant-data-admin/server/rate-limit-store'\nimport { isServerReleaseFlagOnForOrg } from '@aglyn/tenant-data-admin/server/release-flags'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport {\n crawlSiteForImport,\n readSiteVerificationMeta,\n type SiteImportCrawlSeams,\n} from '../site-import/site-import-crawl'\nimport { SITE_IMPORT_RELEASE_FLAG } from '../site-import/site-import-flag'\n\nexport { SITE_IMPORT_RELEASE_FLAG }\n\n/** Pages the crawl door reads in one request; the import job reads in waves. */\nexport const SITE_IMPORT_DOOR_MAX_PAGES = 25\n\n/** Crawls one org may run through the door in a day. */\nexport const SITE_IMPORT_CRAWLS_PER_ORG_PER_DAY = 20\n\n/** DNS and home-page checks one org may run in an hour. */\nexport const SITE_IMPORT_CHECKS_PER_ORG_PER_HOUR = 60\n\n/** The door's own clock for a crawl, inside the route's `maxDuration`. */\nconst SITE_IMPORT_DOOR_TIME_MS = 45_000\n\nexport interface SiteImportGateContext {\n uid: string\n decoded: DecodedIdToken\n staff: boolean\n orgId: string\n org: Partial<AglynOrganization>\n member: AglynOrgMember | null\n firestore: FirebaseFirestore.Firestore\n}\n\n/** What the doors reach outside the process, replaceable in a spec. */\nexport interface SiteImportRouteSeams {\n ownership?: DomainOwnershipDeps\n crawl?: SiteImportCrawlSeams\n}\n\nfunction bearerToken(request: Request): string | undefined {\n const authorization = request.headers.get('authorization') ?? ''\n return authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined\n}\n\nexport async function siteImportGate(\n request: Request,\n rawOrgId: string,\n): Promise<Response | SiteImportGateContext> {\n const idToken = bearerToken(request)\n if (!idToken) return Response.json({ error: 'Unauthenticated' }, { status: 401 })\n const app = firebaseAdmin.app()\n let decoded: DecodedIdToken\n try {\n decoded = await app.auth().verifyIdToken(idToken)\n } catch (error) {\n const unauthenticated = invalidIdTokenResponse(error)\n if (unauthenticated) return unauthenticated\n throw error\n }\n if (!decoded.email_verified && !isImpersonationSession(decoded)) return emailUnverifiedResponse()\n const staff = decoded['staff'] === true\n const orgId = String(rawOrgId ?? '').trim()\n if (!orgId) {\n return Response.json({ error: 'Open a workspace before using this feature' }, { status: 400 })\n }\n const resolved = await getOrgForUser(decoded.uid, orgId)\n if (!resolved || resolved.orgId !== orgId) {\n return Response.json({ error: 'You are not a member of that organization' }, { status: 403 })\n }\n if (!staff && !(await isServerReleaseFlagOnForOrg(SITE_IMPORT_RELEASE_FLAG, orgId))) {\n return Response.json({ error: 'Not found' }, { status: 404 })\n }\n const member = resolved.member ?? null\n if (!member || (member.role !== 'owner' && member.role !== 'admin')) {\n return Response.json(\n { error: 'Only the workspace owner or an admin can bring a site in.', reason: 'role' },\n { status: 403 },\n )\n }\n const org = resolved.org ?? {}\n if (resolveEffectivePlan(org) === 'free') {\n return Response.json(\n { error: 'Bringing an existing site in is included on every paid plan.', reason: 'plan' },\n { status: 403 },\n )\n }\n const locked = await lockdownRefusal({\n request,\n staff,\n uid: decoded.uid,\n org: org as Record<string, unknown>,\n })\n if (locked) return locked\n return { uid: decoded.uid, decoded, staff, orgId, org, member, firestore: app.firestore() }\n}\n\n/**\n * Whether \"Bring my site\" is offered to this reader (AGL-3976), in the\n * doors' own order, for Create site's choice card: `null` while the flag is\n * off (the card is not drawn), `role` or `plan` for a refusal the workspace\n * can change (the card is drawn disabled with the reason), `ready` otherwise.\n * Read by the jobs list only when the card asks, so no other read pays for it.\n */\nexport async function siteImportOfferFor(input: {\n orgId: string\n staff: boolean\n org: Partial<AglynOrganization> | null\n member: AglynOrgMember | null\n}): Promise<'ready' | 'role' | 'plan' | null> {\n try {\n if (!input.staff && !(await isServerReleaseFlagOnForOrg(SITE_IMPORT_RELEASE_FLAG, input.orgId))) return null\n } catch {\n return null\n }\n if (!input.member || (input.member.role !== 'owner' && input.member.role !== 'admin')) return 'role'\n if (resolveEffectivePlan(input.org ?? {}) === 'free') return 'plan'\n return 'ready'\n}\n\nasync function audit(\n gate: SiteImportGateContext,\n action: string,\n domain: string,\n after: Record<string, unknown>,\n): Promise<void> {\n try {\n await addAdminAudit(gate.firestore, {\n actorUid: gate.uid,\n actorEmail: gate.decoded.email ?? null,\n action,\n target: `orgs/${gate.orgId}/domainOwnershipClaims/${domain}`,\n after: { ...after, ...(gate.staff ? { staff: true } : {}) },\n at: FieldValue.serverTimestamp(),\n })\n } catch (error) {\n console.error('site import audit write failed', { orgId: gate.orgId, action, error })\n }\n}\n\nfunction ownershipDeps(seams: SiteImportRouteSeams): DomainOwnershipDeps {\n return {\n readVerificationMeta: (domain) =>\n readSiteVerificationMeta(domain, DOMAIN_OWNERSHIP_META_NAME, seams.crawl),\n ...seams.ownership,\n }\n}\n\nconst tooMany = (error: string) => Response.json({ error, reason: 'rate' }, { status: 429 })\n\n/** `GET /api/ai/site-import/ownership?orgId=`. */\nexport async function listSiteImportOwnership(\n request: Request,\n seams: SiteImportRouteSeams = {},\n): Promise<Response> {\n const orgId = new URL(request.url).searchParams.get('orgId') ?? ''\n const gate = await siteImportGate(request, orgId)\n if (gate instanceof Response) return gate\n const claims = await listDomainOwnershipClaims(gate.orgId, ownershipDeps(seams))\n return Response.json({ claims })\n}\n\n/** `POST /api/ai/site-import/ownership`. */\nexport async function postSiteImportOwnership(\n request: Request,\n seams: SiteImportRouteSeams = {},\n): Promise<Response> {\n const body = (await request.json().catch(() => null)) as Record<string, unknown> | null\n if (!body || typeof body !== 'object') {\n return Response.json({ error: 'Send a JSON body' }, { status: 400 })\n }\n const gate = await siteImportGate(request, String(body['orgId'] ?? ''))\n if (gate instanceof Response) return gate\n const deps = ownershipDeps(seams)\n const action = String(body['action'] ?? '')\n const domain = String(body['domain'] ?? '')\n\n if (action === 'claim') {\n const { claim, error } = await issueDomainOwnershipClaim(gate.orgId, domain, gate.uid, deps)\n if (!claim) return Response.json({ error }, { status: 400 })\n await audit(gate, 'site-import.ownership.claim', claim.domain, { status: claim.status })\n return Response.json({ claim })\n }\n\n if (action === 'check') {\n const method = body['method']\n if (method !== 'dns' && method !== 'meta') {\n return Response.json({ error: 'method must be dns or meta' }, { status: 400 })\n }\n const window = await consumeRateLimit(`site-import-ownership-check:${gate.orgId}`, {\n limit: SITE_IMPORT_CHECKS_PER_ORG_PER_HOUR,\n windowMs: 60 * 60 * 1000,\n })\n if (!window.allowed) return tooMany('Too many checks. Try again in an hour.')\n const { result, error } = await checkDomainOwnership(gate.orgId, domain, method, deps)\n if (!result) return Response.json({ error }, { status: 400 })\n if (result.status === 'proven') {\n await audit(gate, 'site-import.ownership.proven', result.claim.domain, {\n method,\n detail: result.claim.provenDetail,\n })\n }\n return Response.json({ result })\n }\n\n if (action === 'send-code') {\n const sent = await sendDomainOwnershipCode(\n gate.orgId,\n domain,\n String(body['email'] ?? ''),\n { org: gate.org as Record<string, unknown> },\n deps,\n )\n if (!sent.claim) return Response.json({ error: sent.error }, { status: sent.status })\n await audit(gate, 'site-import.ownership.code-sent', sent.claim.domain, {\n address: sent.claim.email.pendingAddress,\n })\n return Response.json({ claim: sent.claim })\n }\n\n if (action === 'confirm-code') {\n const { result, error } = await confirmDomainOwnershipCode(gate.orgId, domain, String(body['code'] ?? ''), deps)\n if (!result) return Response.json({ error }, { status: 400 })\n if (result.status === 'proven') {\n await audit(gate, 'site-import.ownership.proven', result.claim.domain, {\n method: 'email',\n detail: result.claim.provenDetail,\n })\n }\n return Response.json({ result })\n }\n\n return Response.json(\n { error: 'action must be claim, check, send-code or confirm-code' },\n { status: 400 },\n )\n}\n\n/** `POST /api/ai/site-import/crawl`. */\nexport async function postSiteImportCrawl(\n request: Request,\n seams: SiteImportRouteSeams = {},\n): Promise<Response> {\n const body = (await request.json().catch(() => null)) as Record<string, unknown> | null\n if (!body || typeof body !== 'object') {\n return Response.json({ error: 'Send a JSON body' }, { status: 400 })\n }\n const gate = await siteImportGate(request, String(body['orgId'] ?? ''))\n if (gate instanceof Response) return gate\n const { domain, error } = normalizeOwnershipDomain(String(body['domain'] ?? ''))\n if (!domain) return Response.json({ error }, { status: 400 })\n // Ownership BEFORE any page is read: a crawl of a site the org has not\n // proved is exactly the clone this whole feature is built to refuse.\n if (!(await isDomainOwnershipProven(gate.orgId, domain, ownershipDeps(seams)))) {\n return Response.json(\n { error: 'Verify that you own this site first.', reason: 'ownership' },\n { status: 403 },\n )\n }\n const window = await consumeRateLimit(`site-import-crawl:${gate.orgId}`, {\n limit: SITE_IMPORT_CRAWLS_PER_ORG_PER_DAY,\n windowMs: 24 * 60 * 60 * 1000,\n })\n if (!window.allowed) return tooMany('This workspace has read sites enough times today. Try again tomorrow.')\n const planPages = resolveOrgEntitlements(gate.org).screensPerHost\n const asked = Number(body['maxPages'])\n const maxPages = Math.max(\n 1,\n Math.min(\n SITE_IMPORT_DOOR_MAX_PAGES,\n Number.isFinite(planPages) && planPages > 0 ? planPages : SITE_IMPORT_DOOR_MAX_PAGES,\n Number.isFinite(asked) && asked > 0 ? Math.floor(asked) : SITE_IMPORT_DOOR_MAX_PAGES,\n ),\n )\n const inventory = await crawlSiteForImport(\n { domain, maxPages, timeBudgetMs: SITE_IMPORT_DOOR_TIME_MS, signal: request.signal },\n seams.crawl,\n )\n await audit(gate, 'site-import.crawl', domain, {\n pages: inventory.pages.length,\n skipped: inventory.skipped.length + inventory.skippedOverflow,\n loginPagesRefused: inventory.skipped.filter((entry) => entry.reason === 'login-form').length,\n stoppedBy: inventory.stoppedBy,\n refused: inventory.refused,\n bytesRead: inventory.bytesRead,\n })\n return Response.json({ inventory })\n}\n"],"names":["resolveEffectivePlan","resolveOrgEntitlements","addAdminAudit","checkDomainOwnership","confirmDomainOwnershipCode","DOMAIN_OWNERSHIP_META_NAME","isDomainOwnershipProven","issueDomainOwnershipClaim","listDomainOwnershipClaims","normalizeOwnershipDomain","sendDomainOwnershipCode","emailUnverifiedResponse","firebaseAdmin","isImpersonationSession","invalidIdTokenResponse","lockdownRefusal","getOrgForUser","consumeRateLimit","isServerReleaseFlagOnForOrg","FieldValue","crawlSiteForImport","readSiteVerificationMeta","SITE_IMPORT_RELEASE_FLAG","SITE_IMPORT_DOOR_MAX_PAGES","SITE_IMPORT_CRAWLS_PER_ORG_PER_DAY","SITE_IMPORT_CHECKS_PER_ORG_PER_HOUR","SITE_IMPORT_DOOR_TIME_MS","bearerToken","request","authorization","headers","get","startsWith","slice","length","undefined","siteImportGate","rawOrgId","resolved","idToken","Response","json","error","status","app","decoded","auth","verifyIdToken","unauthenticated","email_verified","staff","orgId","String","trim","uid","member","role","reason","org","locked","firestore","siteImportOfferFor","input","audit","gate","action","domain","after","actorUid","actorEmail","email","target","at","serverTimestamp","console","ownershipDeps","seams","readVerificationMeta","crawl","ownership","tooMany","listSiteImportOwnership","URL","url","searchParams","claims","postSiteImportOwnership","body","catch","deps","claim","method","window","limit","windowMs","allowed","result","detail","provenDetail","sent","address","pendingAddress","postSiteImportCrawl","planPages","screensPerHost","asked","Number","maxPages","Math","max","min","isFinite","floor","inventory","timeBudgetMs","signal","pages","skipped","skippedOverflow","loginPagesRefused","filter","entry","stoppedBy","refused","bytesRead"],"mappings":"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"}
|
package/src/lib/server.js
CHANGED
|
@@ -30,6 +30,7 @@ import { registerAiProductsJob } from "./jobs/ai-job-products-step.js";
|
|
|
30
30
|
import { registerAiEmailJob } from "./jobs/ai-job-email-step.js";
|
|
31
31
|
import { registerAiCampaignJob } from "./jobs/ai-job-campaign-step.js";
|
|
32
32
|
import { registerAiSiteJob } from "./jobs/ai-job-site-step.js";
|
|
33
|
+
import { registerAiSiteImportJob } from "./jobs/ai-job-import-step.js";
|
|
33
34
|
import { registerAiBuildJob } from "./jobs/ai-job-build-step.js";
|
|
34
35
|
import { registerAiBuildCapabilities } from "./jobs/ai-build-capabilities.js";
|
|
35
36
|
import { registerAiWorkflowJob } from "./jobs/ai-job-workflow-step.js";
|
|
@@ -73,6 +74,7 @@ import { GET as aiUsage } from "./server/ai-usage.js";
|
|
|
73
74
|
import { GET as aiAllotments } from "./server/ai-allotments.js";
|
|
74
75
|
import { GET as aiInsightAnswer } from "./server/ai-insight-answer.js";
|
|
75
76
|
import { POST as runAiInsightsDigest } from "./server/ai-insight-digest-route.js";
|
|
77
|
+
import { listSiteImportOwnership, postSiteImportCrawl, postSiteImportOwnership } from "./server/ai-site-import-route.js";
|
|
76
78
|
import { AI_INSIGHTS_DIGEST_PATH } from "./insights/ai-insight-digest.js";
|
|
77
79
|
import { GET as aiModels } from "./server/ai-models.js";
|
|
78
80
|
import { GET as billingCredits } from "./server/billing-credits.js";
|
|
@@ -151,6 +153,9 @@ export * from "./runtime/ai-runtime.js";
|
|
|
151
153
|
// The site scaffold, which builds a whole site through the steps above
|
|
152
154
|
// (AGL-2911).
|
|
153
155
|
registerAiSiteJob();
|
|
156
|
+
// Bring my site (AGL-3976): a proved website brought in as draft pages, in
|
|
157
|
+
// waves a person starts one at a time, through the page and layout steps.
|
|
158
|
+
registerAiSiteImportJob();
|
|
154
159
|
// A build from one request (AGL-3616): pages, creations and other
|
|
155
160
|
// plugins' drafts, through the steps above and the writers those plugins
|
|
156
161
|
// register, settled item by item. What this plugin lets a build make is
|
|
@@ -303,6 +308,15 @@ export * from "./runtime/ai-runtime.js";
|
|
|
303
308
|
registerPluginApiRoute('ai/media/images', {
|
|
304
309
|
web: createAiMediaImages
|
|
305
310
|
});
|
|
311
|
+
// "Bring my site" (AGL-3974): proving the workspace owns a website, and
|
|
312
|
+
// reading it once it has. Behind `release_ai_site_import`, paid plans,
|
|
313
|
+
// owners and admins; the crawl refuses a domain without a current proof.
|
|
314
|
+
registerPluginApiRoute('ai/site-import/ownership', {
|
|
315
|
+
web: (request)=>request.method === 'GET' ? listSiteImportOwnership(request) : postSiteImportOwnership(request)
|
|
316
|
+
});
|
|
317
|
+
registerPluginApiRoute('ai/site-import/crawl', {
|
|
318
|
+
web: (request)=>postSiteImportCrawl(request)
|
|
319
|
+
});
|
|
306
320
|
registerPluginApiRoute('ai/billing/credits', {
|
|
307
321
|
web: billingCredits
|
|
308
322
|
});
|
package/src/lib/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/plugins/ai/src/lib/server.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 { registerPluginApiRoute } from '@aglyn/aglyn/server'\nimport { registerAiDeclarations } from './declarations'\nimport { AI_JOBS_BEAT_PATH } from './jobs/ai-jobs-beat'\nimport { registerAiJobPlan } from './jobs/ai-job-plan-step'\nimport { registerAiJobsPause } from './jobs/ai-jobs-pause'\nimport { registerAiJobsNotify } from './jobs/ai-jobs-notify'\nimport { registerAiComponentJob } from './jobs/ai-job-component-step'\nimport { registerAiCrmJob } from './jobs/ai-job-crm-step'\nimport { registerAiLayoutJob } from './jobs/ai-job-layout-step'\nimport { registerAiTemplateJob } from './jobs/ai-job-template-step'\nimport { registerAiExperimentJob } from './jobs/ai-job-experiment-step'\nimport { registerAiFormJob } from './jobs/ai-job-form-step'\nimport { registerAiPageJob } from './jobs/ai-job-page-step'\nimport { registerAiProductsJob } from './jobs/ai-job-products-step'\nimport { registerAiEmailJob } from './jobs/ai-job-email-step'\nimport { registerAiCampaignJob } from './jobs/ai-job-campaign-step'\nimport { registerAiSiteJob } from './jobs/ai-job-site-step'\nimport { registerAiBuildJob } from './jobs/ai-job-build-step'\nimport { registerAiBuildCapabilities } from './jobs/ai-build-capabilities'\nimport { registerAiWorkflowJob } from './jobs/ai-job-workflow-step'\nimport { registerAiLogicJob } from './jobs/ai-job-logic-step'\nimport { registerAiEditJob } from './jobs/ai-job-edit-step'\nimport { registerAiNavigationJob } from './jobs/ai-job-navigation-step'\nimport { registerAiDuplicateJob } from './jobs/ai-job-duplicate-step'\nimport { registerAiSettingsJob } from './jobs/ai-job-settings-step'\nimport { registerAiMediaJob } from './jobs/ai-job-media-step'\nimport { registerAiTrackingJob } from './jobs/ai-job-tracking-step'\nimport { registerAiInsightJob } from './jobs/ai-job-insight-step'\nimport { registerAiFigureReaders } from './insights/ai-figure-readers'\nimport { registerAiTextGenerator } from './server/plugin-text-generation'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { ensureFirstPartyAiProviders } from './providers/registry'\nimport { aiAssistHandler } from './server/ai-assist'\nimport { GET as aiCrmAnswer } from './server/ai-crm-answer'\nimport { POST as runAiJobsBeat } from './server/ai-jobs-beat-route'\nimport { POST as cancelAiJob } from './server/ai-jobs-cancel'\nimport { GET as aiJobEvents } from './server/ai-jobs-events-route'\nimport { POST as createAiSiteBatch } from './server/ai-jobs-batch'\nimport { GET as listAiJobs, POST as createAiJob } from './server/ai-jobs-route'\nimport { GET as aiCreateDoor, GET_FOLLOW as aiCreateFollow } from './server/ai-create-route'\nimport { POST as resumeAiJob } from './server/ai-jobs-resume'\nimport { POST as aiGenerateComponent } from './server/ai-generate-component'\nimport { POST as createAiMediaImages } from './server/ai-media-image'\nimport { POST as applyAiSeoAudit } from './server/ai-seo-apply'\nimport { POST as createAiExperimentVersions } from './server/ai-experiment-versions'\nimport { POST as assistChat } from './server/assist-chat'\nimport { POST as assistEditApplied } from './server/assist-edit-applied'\nimport { POST as assistFeedback } from './server/assist-feedback'\nimport { PATCH as aiHostPermissions } from './server/ai-host-permissions'\nimport { GET as aiAdminOrg } from './server/ai-admin-org'\nimport { GET as aiAdminConversations } from './server/ai-admin-conversations'\nimport { GET as aiAdminOrgsSpend } from './server/ai-admin-orgs-spend'\nimport { GET as aiAdminSignals } from './server/ai-admin-signals'\nimport { GET as aiAdminUser } from './server/ai-admin-user'\nimport { POST as aiAdminOverage } from './server/ai-admin-overage'\nimport { POST as aiAdminCredits } from './server/ai-admin-credits'\nimport { GET as aiUsage } from './server/ai-usage'\nimport { GET as aiAllotments } from './server/ai-allotments'\nimport { GET as aiInsightAnswer } from './server/ai-insight-answer'\nimport { POST as runAiInsightsDigest } from './server/ai-insight-digest-route'\nimport { AI_INSIGHTS_DIGEST_PATH } from './insights/ai-insight-digest'\nimport { GET as aiModels } from './server/ai-models'\nimport { GET as billingCredits } from './server/billing-credits'\nimport { POST as billingOverage } from './server/billing-overage'\n\nexport * from './providers/contract'\nexport * from './providers/catalog'\nexport * from './providers/registry'\nexport * from './providers/routing'\nexport * from './runtime/ai-runtime'\n\n/**\n * The two first-party adapters, registered once per process. A marketplace\n * plugin registers its own against `AI_PROVIDER_CONTRACT` from its own\n * entry and never touches this.\n */\nconst registerFirstPartyProviders = ensureFirstPartyAiProviders\n\n/**\n * Every job kind this plugin runs beyond the three the machine registers\n * itself (AGL-3025). Each is a CALL, never an import made for what the\n * module does as it loads: this package declares only this file\n * effect-ful, so a bundler honoring `sideEffects` deletes an import whose\n * exports go unused — and a step registered that way ran in every spec and\n * in neither app. `registrations-survive-a-bundler.spec.ts` holds this.\n *\n * The console surface is the only caller (AGL-3026): its doors run a job's\n * first step inline, and its beat runs every step after it. The tenant app\n * loads no server surface of this plugin at all, so nothing that serves a\n * published site can reach a provider.\n */\nfunction registerAiJobKinds(): void {\n // The workspace AI pause the machine asks before it claims any step, so a\n // paused workspace's queued jobs spend nothing (AGL-3037).\n registerAiJobsPause()\n // The person who started a job is told when it needs them, finishes or\n // stops (AGL-3593), from the write that changed it.\n registerAiJobsNotify()\n // The plan step every planned kind runs first (AGL-2935).\n registerAiJobPlan()\n // Components, layouts and templates (AGL-2908, AGL-2909).\n registerAiComponentJob()\n registerAiLayoutJob()\n registerAiTemplateJob()\n // Forms (AGL-2913).\n registerAiExperimentJob()\n registerAiFormJob()\n // Pages, with the least time one pass needs (AGL-2907).\n registerAiPageJob()\n // Email designs and campaigns, whose drafts the email and marketing\n // plugins write on the resource-draft seam (AGL-2912).\n registerAiEmailJob()\n registerAiCampaignJob()\n // Automations drafted from a description, and explained, whose drafts the\n // workflows plugin writes on the resource-draft seam (AGL-2919).\n registerAiWorkflowJob()\n // Site functions and variables proposed from a description, and functions\n // explained, as proposals the logic editor opens unsaved (AGL-3603).\n registerAiLogicJob()\n // A change to a page or a layout the site has, from a description, saved\n // where no visitor sees it — the Assist edit rung run as a job (AGL-3616).\n registerAiEditJob()\n // A change to a site's header or footer menu, made by code from a build's\n // item and saved beside the live layout (AGL-3728).\n registerAiNavigationJob()\n // A copy of something a site already has, through core's duplicate — the\n // console's own Duplicate… — as a build's item (AGL-3740).\n registerAiDuplicateJob()\n // A change to a site's settings — its name, search defaults, icons,\n // language, time zone and contact details — saved live (AGL-3748).\n registerAiSettingsJob()\n // Folders, tags, alt text and names of files already in a site's media\n // library, as its Details drawer saves them, live (AGL-3752).\n registerAiMediaJob()\n // The site's analytics and advertising tags, as Setup → Tracking sets\n // them, saved live (AGL-3751).\n registerAiTrackingJob()\n // Product copy, and a store's products, categories and discounts from a\n // brief, as proposals the commerce plugin's surfaces apply (AGL-2916).\n registerAiProductsJob()\n // The site scaffold, which builds a whole site through the steps above\n // (AGL-2911).\n registerAiSiteJob()\n // A build from one request (AGL-3616): pages, creations and other\n // plugins' drafts, through the steps above and the writers those plugins\n // register, settled item by item. What this plugin lets a build make is\n // registered on the core's capability contract like any plugin's.\n registerAiBuildCapabilities()\n registerAiBuildJob()\n // Insights (AGL-2915): a question about a site's figures, answered from\n // tables the figure readers return, and the weekly digest. The readers for\n // the platform's own records — page views, forms, datasets — are this\n // plugin's; every other plugin registers its own from its console surface.\n registerAiFigureReaders(() => firebaseAdmin.app().firestore())\n registerAiInsightJob()\n // CRM by AI (AGL-2917): a record's summary and next step, an email draft\n // and an import's column matches, read through the CRM's facts readers.\n registerAiCrmJob()\n}\n\n/**\n * The console-side API (AGL-2939): every AI door, under the `ai` and\n * `assist` prefixes the plugin owns. The URLs the panel and the besigner\n * call are unchanged — `/api/assist/chat`, `/api/ai/assist`,\n * `/api/ai/jobs` — because the dispatcher serves them from the registry\n * exactly where the named routes used to; the two billing doors and the\n * per-member usage read (`ai/usage`) live under the plugin's own prefix.\n * The jobs beat is the one path outside them, for the reason\n * `AI_JOBS_BEAT_PATH` gives.\n */\nexport function registerAiConsoleApi(): void {\n registerAiDeclarations()\n registerFirstPartyProviders()\n registerAiJobKinds()\n // This plugin as the workspace's text generator on the core's seam\n // (AGL-3324): another plugin drafts a paragraph through it under the\n // generation doors' own rules, metered as a text job is.\n registerAiTextGenerator()\n registerPluginApiRoute('assist/chat', { web: assistChat })\n registerPluginApiRoute('assist/feedback', { web: assistFeedback })\n // The applied-edit record (AGL-2906): counts of a proposal the author\n // applied in their editor, written to the site's activity log.\n registerPluginApiRoute('assist/edit-applied', { web: assistEditApplied })\n // A collaborator's AI toggles on one site (AGL-2927), set from the site's\n // collaborators card through this plugin's column there.\n registerPluginApiRoute('ai/host-permissions', { web: aiHostPermissions })\n registerPluginApiRoute('ai/assist', aiAssistHandler)\n registerPluginApiRoute('ai/jobs', {\n web: (request) => (request.method === 'GET' ? listAiJobs(request) : createAiJob(request)),\n })\n // The agency batch (AGL-2911): one brief, one `site` job per named site,\n // under one batch id. Before the `:jobId` routes, which it is not one of.\n registerPluginApiRoute('ai/jobs/batch', { web: createAiSiteBatch })\n registerPluginApiRoute('ai/jobs/:jobId/cancel', {\n web: (request, context) =>\n cancelAiJob(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n registerPluginApiRoute('ai/jobs/:jobId/resume', {\n web: (request, context) =>\n resumeAiJob(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n // An insight job's answer (AGL-2915), which is kept off the job document\n // and served only to the member who asked, or for a digest to the site's\n // members.\n registerPluginApiRoute('ai/insights/:jobId', {\n web: (request, context) =>\n aiInsightAnswer(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n // A CRM job's answer (AGL-2917), which is kept off the job document and\n // served only to a member the CRM still lets read the record.\n registerPluginApiRoute('ai/crm/:jobId', {\n web: (request, context) =>\n aiCrmAnswer(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n // Create with AI in the apps (AGL-3854): whether a list draws the entry and\n // every word its brief says, and the job it started as the dialog's follow\n // card reads it. Both read; the jobs doors above start, confirm and cancel.\n registerPluginApiRoute('ai/create', { web: aiCreateDoor })\n registerPluginApiRoute('ai/create/:jobId', {\n web: (request, context) =>\n aiCreateFollow(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n registerPluginApiRoute('ai/jobs/:jobId/events', {\n web: (request, context) =>\n aiJobEvents(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n // The jobs beat (AGL-3026): every step the doors leave queued, run every\n // minute by the console's scheduler on the cron secret, on the one surface\n // that holds the provider's key.\n registerPluginApiRoute(AI_JOBS_BEAT_PATH, { web: runAiJobsBeat })\n // The weekly insights (AGL-2915): Monday's digests made for the people who\n // asked for them, and delivered once the beat has written them. Beside the\n // beat under `/api/admin/`, where the console's scheduled sweeps live.\n // A machine's route: Cloud Scheduler calls it on the cron secret with no\n // site and no member, and the route checks that secret and the AI release\n // flag itself. Unmarked, it was counted in the dispatcher's per-address\n // write bucket and refused with a 429 at 14:00Z on 2026-10-09.\n registerPluginApiRoute(AI_INSIGHTS_DIGEST_PATH, { web: runAiInsightsDigest }, { machine: true })\n // A site SEO audit's \"Apply all\" (AGL-2910): content fixes as new\n // unpublished versions, listing values staged for their SEO cards.\n registerPluginApiRoute('ai/seo/apply', { web: applyAiSeoAudit })\n // A page's or a section's A/B variants as draft versions (AGL-3603): the\n // published version copied once per proposed variant, the variant's copy\n // put in, stored unpublished for the experiment editor to pin.\n registerPluginApiRoute('ai/experiments/versions', { web: createAiExperimentVersions })\n // Save the selection as a reusable component, with AI (AGL-2908): the\n // second entry point of the component job, from the besigner's Attributes\n // panel. It proposes, and the person's Apply writes.\n registerPluginApiRoute('ai/generate/component', { web: aiGenerateComponent })\n // Pictures from a description, in Media (AGL-3602): photos and SVG\n // illustrations, stored through the media upload route. POST only — the\n // button draws from the shell's gates and asks nothing until it is used.\n registerPluginApiRoute('ai/media/images', { web: createAiMediaImages })\n registerPluginApiRoute('ai/billing/credits', { web: billingCredits })\n registerPluginApiRoute('ai/billing/overage', { web: billingOverage })\n registerPluginApiRoute('ai/usage', { web: aiUsage })\n // The allotments a manager sets, and the options the model switch lists\n // (AGL-2942). One handler answers the allotments' GET and POST.\n registerPluginApiRoute('ai/allotments', { web: aiAllotments })\n registerPluginApiRoute('ai/models', { web: aiModels })\n // The staff doors (AGL-2928, AGL-2930, AGL-2252): one org's AI in full,\n // one account's usage across its workspaces, and the fleet's Assist\n // signals behind the Assist signal staff page.\n registerPluginApiRoute('ai/admin/org', { web: aiAdminOrg })\n registerPluginApiRoute('ai/admin/orgs-spend', { web: aiAdminOrgsSpend })\n registerPluginApiRoute('ai/admin/user', { web: aiAdminUser })\n registerPluginApiRoute('ai/admin/signals', { web: aiAdminSignals })\n // What an org asked Aglyn AI and what it answered (AGL-3675): one page at a\n // time, read on request, each page an audited access.\n registerPluginApiRoute('ai/admin/conversations', { web: aiAdminConversations })\n // One workspace's overage standing and the four acts staff have over it\n // (AGL-3011): the ceiling override, lifting a pause — the only way a\n // dispute pause comes off — and resetting the step. Every act audited.\n registerPluginApiRoute('ai/admin/overage', { web: aiAdminOverage })\n // Credits given back to a workspace's band or a Free owner's allowance\n // (AGL-3595): compensation for a fault of ours, bounded by what the month\n // used, keyed against a double-click, and audited.\n registerPluginApiRoute('ai/admin/credits', { web: aiAdminCredits })\n}\n\n// Type-only (AGL-3080): the plugin's entitlement keys, declared by module\n// augmentation, for every program that loads this entry point.\nexport type { aiPlanEntitlements } from './plan-entitlements'\n"],"names":["registerPluginApiRoute","registerAiDeclarations","AI_JOBS_BEAT_PATH","registerAiJobPlan","registerAiJobsPause","registerAiJobsNotify","registerAiComponentJob","registerAiCrmJob","registerAiLayoutJob","registerAiTemplateJob","registerAiExperimentJob","registerAiFormJob","registerAiPageJob","registerAiProductsJob","registerAiEmailJob","registerAiCampaignJob","registerAiSiteJob","registerAiBuildJob","registerAiBuildCapabilities","registerAiWorkflowJob","registerAiLogicJob","registerAiEditJob","registerAiNavigationJob","registerAiDuplicateJob","registerAiSettingsJob","registerAiMediaJob","registerAiTrackingJob","registerAiInsightJob","registerAiFigureReaders","registerAiTextGenerator","firebaseAdmin","ensureFirstPartyAiProviders","aiAssistHandler","GET","aiCrmAnswer","POST","runAiJobsBeat","cancelAiJob","aiJobEvents","createAiSiteBatch","listAiJobs","createAiJob","aiCreateDoor","GET_FOLLOW","aiCreateFollow","resumeAiJob","aiGenerateComponent","createAiMediaImages","applyAiSeoAudit","createAiExperimentVersions","assistChat","assistEditApplied","assistFeedback","PATCH","aiHostPermissions","aiAdminOrg","aiAdminConversations","aiAdminOrgsSpend","aiAdminSignals","aiAdminUser","aiAdminOverage","aiAdminCredits","aiUsage","aiAllotments","aiInsightAnswer","runAiInsightsDigest","AI_INSIGHTS_DIGEST_PATH","aiModels","billingCredits","billingOverage","registerFirstPartyProviders","registerAiJobKinds","app","firestore","registerAiConsoleApi","web","request","method","context","params","Promise","resolve","jobId","String","machine"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/plugins/ai/src/lib/server.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 { registerPluginApiRoute } from '@aglyn/aglyn/server'\nimport { registerAiDeclarations } from './declarations'\nimport { AI_JOBS_BEAT_PATH } from './jobs/ai-jobs-beat'\nimport { registerAiJobPlan } from './jobs/ai-job-plan-step'\nimport { registerAiJobsPause } from './jobs/ai-jobs-pause'\nimport { registerAiJobsNotify } from './jobs/ai-jobs-notify'\nimport { registerAiComponentJob } from './jobs/ai-job-component-step'\nimport { registerAiCrmJob } from './jobs/ai-job-crm-step'\nimport { registerAiLayoutJob } from './jobs/ai-job-layout-step'\nimport { registerAiTemplateJob } from './jobs/ai-job-template-step'\nimport { registerAiExperimentJob } from './jobs/ai-job-experiment-step'\nimport { registerAiFormJob } from './jobs/ai-job-form-step'\nimport { registerAiPageJob } from './jobs/ai-job-page-step'\nimport { registerAiProductsJob } from './jobs/ai-job-products-step'\nimport { registerAiEmailJob } from './jobs/ai-job-email-step'\nimport { registerAiCampaignJob } from './jobs/ai-job-campaign-step'\nimport { registerAiSiteJob } from './jobs/ai-job-site-step'\nimport { registerAiSiteImportJob } from './jobs/ai-job-import-step'\nimport { registerAiBuildJob } from './jobs/ai-job-build-step'\nimport { registerAiBuildCapabilities } from './jobs/ai-build-capabilities'\nimport { registerAiWorkflowJob } from './jobs/ai-job-workflow-step'\nimport { registerAiLogicJob } from './jobs/ai-job-logic-step'\nimport { registerAiEditJob } from './jobs/ai-job-edit-step'\nimport { registerAiNavigationJob } from './jobs/ai-job-navigation-step'\nimport { registerAiDuplicateJob } from './jobs/ai-job-duplicate-step'\nimport { registerAiSettingsJob } from './jobs/ai-job-settings-step'\nimport { registerAiMediaJob } from './jobs/ai-job-media-step'\nimport { registerAiTrackingJob } from './jobs/ai-job-tracking-step'\nimport { registerAiInsightJob } from './jobs/ai-job-insight-step'\nimport { registerAiFigureReaders } from './insights/ai-figure-readers'\nimport { registerAiTextGenerator } from './server/plugin-text-generation'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { ensureFirstPartyAiProviders } from './providers/registry'\nimport { aiAssistHandler } from './server/ai-assist'\nimport { GET as aiCrmAnswer } from './server/ai-crm-answer'\nimport { POST as runAiJobsBeat } from './server/ai-jobs-beat-route'\nimport { POST as cancelAiJob } from './server/ai-jobs-cancel'\nimport { GET as aiJobEvents } from './server/ai-jobs-events-route'\nimport { POST as createAiSiteBatch } from './server/ai-jobs-batch'\nimport { GET as listAiJobs, POST as createAiJob } from './server/ai-jobs-route'\nimport { GET as aiCreateDoor, GET_FOLLOW as aiCreateFollow } from './server/ai-create-route'\nimport { POST as resumeAiJob } from './server/ai-jobs-resume'\nimport { POST as aiGenerateComponent } from './server/ai-generate-component'\nimport { POST as createAiMediaImages } from './server/ai-media-image'\nimport { POST as applyAiSeoAudit } from './server/ai-seo-apply'\nimport { POST as createAiExperimentVersions } from './server/ai-experiment-versions'\nimport { POST as assistChat } from './server/assist-chat'\nimport { POST as assistEditApplied } from './server/assist-edit-applied'\nimport { POST as assistFeedback } from './server/assist-feedback'\nimport { PATCH as aiHostPermissions } from './server/ai-host-permissions'\nimport { GET as aiAdminOrg } from './server/ai-admin-org'\nimport { GET as aiAdminConversations } from './server/ai-admin-conversations'\nimport { GET as aiAdminOrgsSpend } from './server/ai-admin-orgs-spend'\nimport { GET as aiAdminSignals } from './server/ai-admin-signals'\nimport { GET as aiAdminUser } from './server/ai-admin-user'\nimport { POST as aiAdminOverage } from './server/ai-admin-overage'\nimport { POST as aiAdminCredits } from './server/ai-admin-credits'\nimport { GET as aiUsage } from './server/ai-usage'\nimport { GET as aiAllotments } from './server/ai-allotments'\nimport { GET as aiInsightAnswer } from './server/ai-insight-answer'\nimport { POST as runAiInsightsDigest } from './server/ai-insight-digest-route'\nimport {\n listSiteImportOwnership,\n postSiteImportCrawl,\n postSiteImportOwnership,\n} from './server/ai-site-import-route'\nimport { AI_INSIGHTS_DIGEST_PATH } from './insights/ai-insight-digest'\nimport { GET as aiModels } from './server/ai-models'\nimport { GET as billingCredits } from './server/billing-credits'\nimport { POST as billingOverage } from './server/billing-overage'\n\nexport * from './providers/contract'\nexport * from './providers/catalog'\nexport * from './providers/registry'\nexport * from './providers/routing'\nexport * from './runtime/ai-runtime'\n\n/**\n * The two first-party adapters, registered once per process. A marketplace\n * plugin registers its own against `AI_PROVIDER_CONTRACT` from its own\n * entry and never touches this.\n */\nconst registerFirstPartyProviders = ensureFirstPartyAiProviders\n\n/**\n * Every job kind this plugin runs beyond the three the machine registers\n * itself (AGL-3025). Each is a CALL, never an import made for what the\n * module does as it loads: this package declares only this file\n * effect-ful, so a bundler honoring `sideEffects` deletes an import whose\n * exports go unused — and a step registered that way ran in every spec and\n * in neither app. `registrations-survive-a-bundler.spec.ts` holds this.\n *\n * The console surface is the only caller (AGL-3026): its doors run a job's\n * first step inline, and its beat runs every step after it. The tenant app\n * loads no server surface of this plugin at all, so nothing that serves a\n * published site can reach a provider.\n */\nfunction registerAiJobKinds(): void {\n // The workspace AI pause the machine asks before it claims any step, so a\n // paused workspace's queued jobs spend nothing (AGL-3037).\n registerAiJobsPause()\n // The person who started a job is told when it needs them, finishes or\n // stops (AGL-3593), from the write that changed it.\n registerAiJobsNotify()\n // The plan step every planned kind runs first (AGL-2935).\n registerAiJobPlan()\n // Components, layouts and templates (AGL-2908, AGL-2909).\n registerAiComponentJob()\n registerAiLayoutJob()\n registerAiTemplateJob()\n // Forms (AGL-2913).\n registerAiExperimentJob()\n registerAiFormJob()\n // Pages, with the least time one pass needs (AGL-2907).\n registerAiPageJob()\n // Email designs and campaigns, whose drafts the email and marketing\n // plugins write on the resource-draft seam (AGL-2912).\n registerAiEmailJob()\n registerAiCampaignJob()\n // Automations drafted from a description, and explained, whose drafts the\n // workflows plugin writes on the resource-draft seam (AGL-2919).\n registerAiWorkflowJob()\n // Site functions and variables proposed from a description, and functions\n // explained, as proposals the logic editor opens unsaved (AGL-3603).\n registerAiLogicJob()\n // A change to a page or a layout the site has, from a description, saved\n // where no visitor sees it — the Assist edit rung run as a job (AGL-3616).\n registerAiEditJob()\n // A change to a site's header or footer menu, made by code from a build's\n // item and saved beside the live layout (AGL-3728).\n registerAiNavigationJob()\n // A copy of something a site already has, through core's duplicate — the\n // console's own Duplicate… — as a build's item (AGL-3740).\n registerAiDuplicateJob()\n // A change to a site's settings — its name, search defaults, icons,\n // language, time zone and contact details — saved live (AGL-3748).\n registerAiSettingsJob()\n // Folders, tags, alt text and names of files already in a site's media\n // library, as its Details drawer saves them, live (AGL-3752).\n registerAiMediaJob()\n // The site's analytics and advertising tags, as Setup → Tracking sets\n // them, saved live (AGL-3751).\n registerAiTrackingJob()\n // Product copy, and a store's products, categories and discounts from a\n // brief, as proposals the commerce plugin's surfaces apply (AGL-2916).\n registerAiProductsJob()\n // The site scaffold, which builds a whole site through the steps above\n // (AGL-2911).\n registerAiSiteJob()\n // Bring my site (AGL-3976): a proved website brought in as draft pages, in\n // waves a person starts one at a time, through the page and layout steps.\n registerAiSiteImportJob()\n // A build from one request (AGL-3616): pages, creations and other\n // plugins' drafts, through the steps above and the writers those plugins\n // register, settled item by item. What this plugin lets a build make is\n // registered on the core's capability contract like any plugin's.\n registerAiBuildCapabilities()\n registerAiBuildJob()\n // Insights (AGL-2915): a question about a site's figures, answered from\n // tables the figure readers return, and the weekly digest. The readers for\n // the platform's own records — page views, forms, datasets — are this\n // plugin's; every other plugin registers its own from its console surface.\n registerAiFigureReaders(() => firebaseAdmin.app().firestore())\n registerAiInsightJob()\n // CRM by AI (AGL-2917): a record's summary and next step, an email draft\n // and an import's column matches, read through the CRM's facts readers.\n registerAiCrmJob()\n}\n\n/**\n * The console-side API (AGL-2939): every AI door, under the `ai` and\n * `assist` prefixes the plugin owns. The URLs the panel and the besigner\n * call are unchanged — `/api/assist/chat`, `/api/ai/assist`,\n * `/api/ai/jobs` — because the dispatcher serves them from the registry\n * exactly where the named routes used to; the two billing doors and the\n * per-member usage read (`ai/usage`) live under the plugin's own prefix.\n * The jobs beat is the one path outside them, for the reason\n * `AI_JOBS_BEAT_PATH` gives.\n */\nexport function registerAiConsoleApi(): void {\n registerAiDeclarations()\n registerFirstPartyProviders()\n registerAiJobKinds()\n // This plugin as the workspace's text generator on the core's seam\n // (AGL-3324): another plugin drafts a paragraph through it under the\n // generation doors' own rules, metered as a text job is.\n registerAiTextGenerator()\n registerPluginApiRoute('assist/chat', { web: assistChat })\n registerPluginApiRoute('assist/feedback', { web: assistFeedback })\n // The applied-edit record (AGL-2906): counts of a proposal the author\n // applied in their editor, written to the site's activity log.\n registerPluginApiRoute('assist/edit-applied', { web: assistEditApplied })\n // A collaborator's AI toggles on one site (AGL-2927), set from the site's\n // collaborators card through this plugin's column there.\n registerPluginApiRoute('ai/host-permissions', { web: aiHostPermissions })\n registerPluginApiRoute('ai/assist', aiAssistHandler)\n registerPluginApiRoute('ai/jobs', {\n web: (request) => (request.method === 'GET' ? listAiJobs(request) : createAiJob(request)),\n })\n // The agency batch (AGL-2911): one brief, one `site` job per named site,\n // under one batch id. Before the `:jobId` routes, which it is not one of.\n registerPluginApiRoute('ai/jobs/batch', { web: createAiSiteBatch })\n registerPluginApiRoute('ai/jobs/:jobId/cancel', {\n web: (request, context) =>\n cancelAiJob(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n registerPluginApiRoute('ai/jobs/:jobId/resume', {\n web: (request, context) =>\n resumeAiJob(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n // An insight job's answer (AGL-2915), which is kept off the job document\n // and served only to the member who asked, or for a digest to the site's\n // members.\n registerPluginApiRoute('ai/insights/:jobId', {\n web: (request, context) =>\n aiInsightAnswer(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n // A CRM job's answer (AGL-2917), which is kept off the job document and\n // served only to a member the CRM still lets read the record.\n registerPluginApiRoute('ai/crm/:jobId', {\n web: (request, context) =>\n aiCrmAnswer(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n // Create with AI in the apps (AGL-3854): whether a list draws the entry and\n // every word its brief says, and the job it started as the dialog's follow\n // card reads it. Both read; the jobs doors above start, confirm and cancel.\n registerPluginApiRoute('ai/create', { web: aiCreateDoor })\n registerPluginApiRoute('ai/create/:jobId', {\n web: (request, context) =>\n aiCreateFollow(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n registerPluginApiRoute('ai/jobs/:jobId/events', {\n web: (request, context) =>\n aiJobEvents(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n // The jobs beat (AGL-3026): every step the doors leave queued, run every\n // minute by the console's scheduler on the cron secret, on the one surface\n // that holds the provider's key.\n registerPluginApiRoute(AI_JOBS_BEAT_PATH, { web: runAiJobsBeat })\n // The weekly insights (AGL-2915): Monday's digests made for the people who\n // asked for them, and delivered once the beat has written them. Beside the\n // beat under `/api/admin/`, where the console's scheduled sweeps live.\n // A machine's route: Cloud Scheduler calls it on the cron secret with no\n // site and no member, and the route checks that secret and the AI release\n // flag itself. Unmarked, it was counted in the dispatcher's per-address\n // write bucket and refused with a 429 at 14:00Z on 2026-10-09.\n registerPluginApiRoute(AI_INSIGHTS_DIGEST_PATH, { web: runAiInsightsDigest }, { machine: true })\n // A site SEO audit's \"Apply all\" (AGL-2910): content fixes as new\n // unpublished versions, listing values staged for their SEO cards.\n registerPluginApiRoute('ai/seo/apply', { web: applyAiSeoAudit })\n // A page's or a section's A/B variants as draft versions (AGL-3603): the\n // published version copied once per proposed variant, the variant's copy\n // put in, stored unpublished for the experiment editor to pin.\n registerPluginApiRoute('ai/experiments/versions', { web: createAiExperimentVersions })\n // Save the selection as a reusable component, with AI (AGL-2908): the\n // second entry point of the component job, from the besigner's Attributes\n // panel. It proposes, and the person's Apply writes.\n registerPluginApiRoute('ai/generate/component', { web: aiGenerateComponent })\n // Pictures from a description, in Media (AGL-3602): photos and SVG\n // illustrations, stored through the media upload route. POST only — the\n // button draws from the shell's gates and asks nothing until it is used.\n registerPluginApiRoute('ai/media/images', { web: createAiMediaImages })\n // \"Bring my site\" (AGL-3974): proving the workspace owns a website, and\n // reading it once it has. Behind `release_ai_site_import`, paid plans,\n // owners and admins; the crawl refuses a domain without a current proof.\n registerPluginApiRoute('ai/site-import/ownership', {\n web: (request) =>\n request.method === 'GET' ? listSiteImportOwnership(request) : postSiteImportOwnership(request),\n })\n registerPluginApiRoute('ai/site-import/crawl', { web: (request) => postSiteImportCrawl(request) })\n registerPluginApiRoute('ai/billing/credits', { web: billingCredits })\n registerPluginApiRoute('ai/billing/overage', { web: billingOverage })\n registerPluginApiRoute('ai/usage', { web: aiUsage })\n // The allotments a manager sets, and the options the model switch lists\n // (AGL-2942). One handler answers the allotments' GET and POST.\n registerPluginApiRoute('ai/allotments', { web: aiAllotments })\n registerPluginApiRoute('ai/models', { web: aiModels })\n // The staff doors (AGL-2928, AGL-2930, AGL-2252): one org's AI in full,\n // one account's usage across its workspaces, and the fleet's Assist\n // signals behind the Assist signal staff page.\n registerPluginApiRoute('ai/admin/org', { web: aiAdminOrg })\n registerPluginApiRoute('ai/admin/orgs-spend', { web: aiAdminOrgsSpend })\n registerPluginApiRoute('ai/admin/user', { web: aiAdminUser })\n registerPluginApiRoute('ai/admin/signals', { web: aiAdminSignals })\n // What an org asked Aglyn AI and what it answered (AGL-3675): one page at a\n // time, read on request, each page an audited access.\n registerPluginApiRoute('ai/admin/conversations', { web: aiAdminConversations })\n // One workspace's overage standing and the four acts staff have over it\n // (AGL-3011): the ceiling override, lifting a pause — the only way a\n // dispute pause comes off — and resetting the step. Every act audited.\n registerPluginApiRoute('ai/admin/overage', { web: aiAdminOverage })\n // Credits given back to a workspace's band or a Free owner's allowance\n // (AGL-3595): compensation for a fault of ours, bounded by what the month\n // used, keyed against a double-click, and audited.\n registerPluginApiRoute('ai/admin/credits', { web: aiAdminCredits })\n}\n\n// Type-only (AGL-3080): the plugin's entitlement keys, declared by module\n// augmentation, for every program that loads this entry point.\nexport type { aiPlanEntitlements } from './plan-entitlements'\n"],"names":["registerPluginApiRoute","registerAiDeclarations","AI_JOBS_BEAT_PATH","registerAiJobPlan","registerAiJobsPause","registerAiJobsNotify","registerAiComponentJob","registerAiCrmJob","registerAiLayoutJob","registerAiTemplateJob","registerAiExperimentJob","registerAiFormJob","registerAiPageJob","registerAiProductsJob","registerAiEmailJob","registerAiCampaignJob","registerAiSiteJob","registerAiSiteImportJob","registerAiBuildJob","registerAiBuildCapabilities","registerAiWorkflowJob","registerAiLogicJob","registerAiEditJob","registerAiNavigationJob","registerAiDuplicateJob","registerAiSettingsJob","registerAiMediaJob","registerAiTrackingJob","registerAiInsightJob","registerAiFigureReaders","registerAiTextGenerator","firebaseAdmin","ensureFirstPartyAiProviders","aiAssistHandler","GET","aiCrmAnswer","POST","runAiJobsBeat","cancelAiJob","aiJobEvents","createAiSiteBatch","listAiJobs","createAiJob","aiCreateDoor","GET_FOLLOW","aiCreateFollow","resumeAiJob","aiGenerateComponent","createAiMediaImages","applyAiSeoAudit","createAiExperimentVersions","assistChat","assistEditApplied","assistFeedback","PATCH","aiHostPermissions","aiAdminOrg","aiAdminConversations","aiAdminOrgsSpend","aiAdminSignals","aiAdminUser","aiAdminOverage","aiAdminCredits","aiUsage","aiAllotments","aiInsightAnswer","runAiInsightsDigest","listSiteImportOwnership","postSiteImportCrawl","postSiteImportOwnership","AI_INSIGHTS_DIGEST_PATH","aiModels","billingCredits","billingOverage","registerFirstPartyProviders","registerAiJobKinds","app","firestore","registerAiConsoleApi","web","request","method","context","params","Promise","resolve","jobId","String","machine"],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
* Recorded crawls for the site-import specs (AGL-3974): what each address
|
|
19
|
+
* answered, status, headers and body, for four small-business sites. No
|
|
20
|
+
* spec opens a socket; `site-import-crawl.spec.ts` serves these through a
|
|
21
|
+
* stubbed `fetch`, under the real SSRF-guarded fetch.
|
|
22
|
+
*
|
|
23
|
+
* - `rosies-bakery.com`, a Wix-style site: the bare name redirects to www;
|
|
24
|
+
* robots.txt keeps crawlers off /cart and names a sitemap index; the home
|
|
25
|
+
* page carries scripts, an iframe, a contact form and a card form; a
|
|
26
|
+
* members' sign-in page; an order page that redirects to another origin;
|
|
27
|
+
* a page past the byte cap; a gallery only a link reaches.
|
|
28
|
+
* - `northside-yoga.com`, a Squarespace-style site whose robots.txt keeps
|
|
29
|
+
* AglynBot off everything while allowing every other crawler.
|
|
30
|
+
* - `plain-plumbing.com`: no robots.txt, no sitemap; pages are found by links.
|
|
31
|
+
* - `down-robots.com`: robots.txt answers 503.
|
|
32
|
+
*/
|
|
33
|
+
export interface RecordedResponse {
|
|
34
|
+
status: number;
|
|
35
|
+
headers?: Record<string, string>;
|
|
36
|
+
body?: string;
|
|
37
|
+
}
|
|
38
|
+
export declare const ROSIES_HOME: string;
|
|
39
|
+
export declare const ROSIES_ABOUT: string;
|
|
40
|
+
export declare const ROSIES_MENU: string;
|
|
41
|
+
export declare const ROSIES_GALLERY: string;
|
|
42
|
+
export declare const ROSIES_LOGIN: string;
|
|
43
|
+
/** Over the specs' 64 KB per-page cap, and honest about it in its header. */
|
|
44
|
+
export declare const ROSIES_BIG: string;
|
|
45
|
+
export declare const SITE_IMPORT_RECORDINGS: Record<string, RecordedResponse>;
|