@aglyn/plugins-ai 1.0.0-beta.261 → 1.0.0-beta.263
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/components/ai-brief-dialog.component.d.ts +21 -58
- package/src/lib/components/ai-brief-dialog.component.js +8 -132
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-job-follow.component.js +2 -35
- package/src/lib/components/ai-job-follow.component.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.d.ts +2 -8
- package/src/lib/components/ai-job-plan.component.js +13 -74
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-template-collection-field.component.d.ts +2 -12
- package/src/lib/components/ai-template-collection-field.component.js +13 -10
- package/src/lib/components/ai-template-collection-field.component.js.map +1 -1
- package/src/lib/components/ai-upsell-dialog.component.d.ts +3 -25
- package/src/lib/components/ai-upsell-dialog.component.js +9 -66
- package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
- package/src/lib/components/use-ai-job-run.d.ts +3 -8
- package/src/lib/components/use-ai-job-run.js +4 -15
- package/src/lib/components/use-ai-job-run.js.map +1 -1
- package/src/lib/insights/ai-figure-readers.js +15 -5
- package/src/lib/insights/ai-figure-readers.js.map +1 -1
- package/src/lib/insights/ai-figure-summaries.d.ts +27 -0
- package/src/lib/insights/ai-figure-summaries.js +96 -0
- package/src/lib/insights/ai-figure-summaries.js.map +1 -0
- package/src/lib/insights/ai-insight-digest.d.ts +2 -0
- package/src/lib/insights/ai-insight-digest.js +7 -4
- package/src/lib/insights/ai-insight-digest.js.map +1 -1
- package/src/lib/insights/ai-insight-readers.d.ts +2 -1
- package/src/lib/insights/ai-insight-readers.js +11 -26
- package/src/lib/insights/ai-insight-readers.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.d.ts +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +5 -3
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +2 -0
- package/src/lib/jobs/ai-job-page-language.js +23 -2
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +14 -1
- package/src/lib/jobs/ai-job-plan-step.js +44 -9
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs-notify.d.ts +8 -1
- package/src/lib/jobs/ai-jobs-notify.js +44 -2
- package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.js +11 -1
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/layout-language/ai-layout-compiler.js +18 -7
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-promised-copy.d.ts +54 -0
- package/src/lib/layout-language/ai-layout-promised-copy.js +104 -0
- package/src/lib/layout-language/ai-layout-promised-copy.js.map +1 -0
- package/src/lib/model/ai-brief-copy.d.ts +85 -0
- package/src/lib/model/ai-brief-copy.js +152 -0
- package/src/lib/model/ai-brief-copy.js.map +1 -0
- package/src/lib/model/ai-create-view.d.ts +200 -0
- package/src/lib/model/ai-create-view.js +263 -0
- package/src/lib/model/ai-create-view.js.map +1 -0
- package/src/lib/model/ai-insight.d.ts +10 -1
- package/src/lib/model/ai-insight.js +16 -39
- package/src/lib/model/ai-insight.js.map +1 -1
- package/src/lib/model/ai-job-activity.d.ts +24 -0
- package/src/lib/model/ai-job-activity.js +52 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +7 -1
- package/src/lib/model/ai-job-notice.js +10 -9
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-job-plan-lines.d.ts +46 -0
- package/src/lib/model/ai-job-plan-lines.js +70 -0
- package/src/lib/model/ai-job-plan-lines.js.map +1 -0
- package/src/lib/model/ai-plan-said-sections.d.ts +81 -0
- package/src/lib/model/ai-plan-said-sections.js +168 -0
- package/src/lib/model/ai-plan-said-sections.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +8 -2
- package/src/lib/model/ai-site-job.js +7 -1
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-template-collections.d.ts +28 -0
- package/src/lib/model/ai-template-collections.js +28 -0
- package/src/lib/model/ai-template-collections.js.map +1 -0
- package/src/lib/model/ai-upsell-copy.d.ts +41 -0
- package/src/lib/model/ai-upsell-copy.js +85 -0
- package/src/lib/model/ai-upsell-copy.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +15 -1
- package/src/lib/runtime/ai-doctrine-validators.js +96 -3
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-eval-memory-firestore.js +2 -0
- package/src/lib/runtime/ai-eval-memory-firestore.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +11 -3
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/server/ai-create-route.d.ts +32 -0
- package/src/lib/server/ai-create-route.js +295 -0
- package/src/lib/server/ai-create-route.js.map +1 -0
- package/src/lib/server.js +14 -0
- package/src/lib/server.js.map +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { AglynOrganization } from '@aglyn/aglyn/foundation/definitions/organization.types';
|
|
18
|
+
/**
|
|
19
|
+
* Whether an add-on this workspace can buy grants `aiGenerative`: the
|
|
20
|
+
* console's `entitlementPurchasable`, over the add-ons the AI plugin declares
|
|
21
|
+
* on the server.
|
|
22
|
+
*/
|
|
23
|
+
export declare function aiGenerativePurchasable(org: Partial<AglynOrganization>): boolean;
|
|
24
|
+
/** The door: whether a list draws Create with AI, as what, and every word its brief says. */
|
|
25
|
+
export declare function GET(request: Request): Promise<Response>;
|
|
26
|
+
/** The follow view: one job of the workspace's, as the dialog that started it reads it. */
|
|
27
|
+
export declare function GET_FOLLOW(request: Request, { params }: {
|
|
28
|
+
params: Promise<{
|
|
29
|
+
jobId: string;
|
|
30
|
+
}>;
|
|
31
|
+
}): Promise<Response>;
|
|
32
|
+
export declare const dynamic = "force-dynamic";
|
|
@@ -0,0 +1,295 @@
|
|
|
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
|
+
*/ // lockdown-423: via libs/plugins/ai/src/lib/server/ai-jobs-gate.ts
|
|
17
|
+
// The follow view climbs `aiJobsGate`, whose lockdown rung is the verdict. The
|
|
18
|
+
// door answers what a list draws and starts nothing: a locked workspace's
|
|
19
|
+
// start is refused by the jobs route's own ladder, in its own words.
|
|
20
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
21
|
+
import { checkEntitlement } from "@aglyn/aglyn/server";
|
|
22
|
+
import { COLLECTIONS_MAX_PER_HOST } from "@aglyn/aglyn/app-utils/collection-entries";
|
|
23
|
+
import { aiAddonName, isUncappedPlanComp, orgSubscriptionState, resolveEffectivePlan } from "@aglyn/aglyn/app-utils/plan-entitlements";
|
|
24
|
+
import { listPluginSeatAddons } from "@aglyn/aglyn/plugin-manager/plugin-entitlements";
|
|
25
|
+
import { emailUnverifiedResponse, firebaseAdmin, isImpersonationSession } from "@aglyn/tenant-data-admin/server/firebase-admin";
|
|
26
|
+
import { invalidIdTokenResponse } from "@aglyn/tenant-data-admin/server/id-token-refusal";
|
|
27
|
+
import { getOrgForUser, memberHasPermissionOnHost } from "@aglyn/tenant-data-admin/server/organizations";
|
|
28
|
+
import { isServerReleaseFlagOnForOrg } from "@aglyn/tenant-data-admin/server/release-flags";
|
|
29
|
+
import { aiJobSiteRefusal } from "../jobs/ai-job-admission.js";
|
|
30
|
+
import { aiJobSummary, getAiJob, listAiJobs } from "../jobs/ai-jobs.js";
|
|
31
|
+
import { aiCreateDoorView, aiJobFollowView, isAiCreateKind } from "../model/ai-create-view.js";
|
|
32
|
+
import { AI_JOB_ACTIVE_STATUSES, AI_PAGE_BUILDING_JOB_KINDS, aiSiteBuildInFlight } from "../model/ai-job-activity.js";
|
|
33
|
+
import { aiTemplateCollectionOptions } from "../model/ai-template-collections.js";
|
|
34
|
+
import { assistSpendAfterReturnsUsd, ASSIST_RETURNED_USD_FIELD } from "../usage/assist-credit-returns.js";
|
|
35
|
+
import { assistBandRefuses, publicAssistCredits, resolveAssistBudgetUsd } from "../usage/assist-credits.js";
|
|
36
|
+
import { readFreeAiCreditsLeft } from "../usage/free-ai-credits-left.js";
|
|
37
|
+
import { aiJobsGate } from "./ai-jobs-gate.js";
|
|
38
|
+
/**
|
|
39
|
+
* `GET /api/ai/create?orgId&hostId&kind` — the door one list's "Create with
|
|
40
|
+
* AI" reads in the apps (AGL-3854), and `GET /api/ai/create/:jobId?orgId` —
|
|
41
|
+
* the job it started, as the dialog's follow card reads it.
|
|
42
|
+
*
|
|
43
|
+
* The console draws each entry from its shell's gates and its own components
|
|
44
|
+
* (`ai-describe-button`, `ai-brief-dialog`, `ai-job-follow`); the apps have
|
|
45
|
+
* neither, so these answer the same verdicts and the same words as data
|
|
46
|
+
* (`model/ai-create-view.ts`). Both only read. Starting, confirming and
|
|
47
|
+
* canceling go through the jobs routes the console calls, whose ladder decides
|
|
48
|
+
* every request exactly as it does for the console.
|
|
49
|
+
*/ function bearerToken(request) {
|
|
50
|
+
var _request_headers_get;
|
|
51
|
+
const authorization = (_request_headers_get = request.headers.get('authorization')) != null ? _request_headers_get : '';
|
|
52
|
+
return authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
|
|
53
|
+
}
|
|
54
|
+
async function doorCaller(request, orgId) {
|
|
55
|
+
var _resolved_org, _resolved_member;
|
|
56
|
+
const idToken = bearerToken(request);
|
|
57
|
+
if (!idToken) return Response.json({
|
|
58
|
+
error: 'Unauthenticated'
|
|
59
|
+
}, {
|
|
60
|
+
status: 401
|
|
61
|
+
});
|
|
62
|
+
let decoded;
|
|
63
|
+
try {
|
|
64
|
+
decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
const unauthenticated = invalidIdTokenResponse(error);
|
|
67
|
+
if (unauthenticated) return unauthenticated;
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
if (!decoded.email_verified && !isImpersonationSession(decoded)) return emailUnverifiedResponse();
|
|
71
|
+
if (!orgId) return Response.json({
|
|
72
|
+
error: 'Open a workspace before using this feature'
|
|
73
|
+
}, {
|
|
74
|
+
status: 400
|
|
75
|
+
});
|
|
76
|
+
const resolved = await getOrgForUser(decoded.uid, orgId);
|
|
77
|
+
if (!resolved || resolved.orgId !== orgId) {
|
|
78
|
+
return Response.json({
|
|
79
|
+
error: 'You are not a member of that organization'
|
|
80
|
+
}, {
|
|
81
|
+
status: 403
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
decoded,
|
|
86
|
+
staff: decoded['staff'] === true,
|
|
87
|
+
orgId,
|
|
88
|
+
org: (_resolved_org = resolved.org) != null ? _resolved_org : {},
|
|
89
|
+
member: (_resolved_member = resolved.member) != null ? _resolved_member : null
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Whether an add-on this workspace can buy grants `aiGenerative`: the
|
|
94
|
+
* console's `entitlementPurchasable`, over the add-ons the AI plugin declares
|
|
95
|
+
* on the server.
|
|
96
|
+
*/ export function aiGenerativePurchasable(org) {
|
|
97
|
+
const billing = org;
|
|
98
|
+
if (orgSubscriptionState(billing) !== 'live') return false;
|
|
99
|
+
const plan = resolveEffectivePlan(billing);
|
|
100
|
+
return listPluginSeatAddons().some((addon)=>{
|
|
101
|
+
var _addon_quota_perUnitByPlan_plan;
|
|
102
|
+
var _addon_features;
|
|
103
|
+
return ((_addon_features = addon.features) == null ? void 0 : _addon_features.includes('aiGenerative')) && (!addon.quota || ((_addon_quota_perUnitByPlan_plan = addon.quota.perUnitByPlan[plan]) != null ? _addon_quota_perUnitByPlan_plan : 0) > 0);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/** What is left of the month's credits, as the AI credits meter reads it. */ async function creditsFacts(firestore, orgId, org, now) {
|
|
107
|
+
if (resolveEffectivePlan(org) === 'free') {
|
|
108
|
+
const left = await readFreeAiCreditsLeft(firestore, {
|
|
109
|
+
orgId,
|
|
110
|
+
org: org,
|
|
111
|
+
now
|
|
112
|
+
});
|
|
113
|
+
return left ? {
|
|
114
|
+
kind: 'free',
|
|
115
|
+
left
|
|
116
|
+
} : {
|
|
117
|
+
kind: 'none'
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const budgetUsd = resolveAssistBudgetUsd(org);
|
|
121
|
+
if (budgetUsd === null) return isUncappedPlanComp(org) ? {
|
|
122
|
+
kind: 'unlimited'
|
|
123
|
+
} : {
|
|
124
|
+
kind: 'none'
|
|
125
|
+
};
|
|
126
|
+
const usage = await firestore.collection('orgs').doc(orgId).collection('assistUsage').doc(now.toISOString().slice(0, 7)).get();
|
|
127
|
+
const credits = publicAssistCredits(assistSpendAfterReturnsUsd(usage.get('estCostUsd'), usage.get(ASSIST_RETURNED_USD_FIELD)), budgetUsd);
|
|
128
|
+
if (credits.limit === null || credits.remaining === null) return {
|
|
129
|
+
kind: 'none'
|
|
130
|
+
};
|
|
131
|
+
return {
|
|
132
|
+
kind: 'band',
|
|
133
|
+
used: credits.used,
|
|
134
|
+
limit: credits.limit,
|
|
135
|
+
remaining: credits.remaining,
|
|
136
|
+
stopsAtBand: assistBandRefuses(org)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const ID_CHARS = /^[A-Za-z0-9_-]{1,100}$/;
|
|
140
|
+
/** The door: whether a list draws Create with AI, as what, and every word its brief says. */ export async function GET(request) {
|
|
141
|
+
var _url_searchParams_get, _url_searchParams_get1, _url_searchParams_get2;
|
|
142
|
+
const url = new URL(request.url);
|
|
143
|
+
const kind = (_url_searchParams_get = url.searchParams.get('kind')) != null ? _url_searchParams_get : '';
|
|
144
|
+
if (!isAiCreateKind(kind)) {
|
|
145
|
+
return Response.json({
|
|
146
|
+
error: 'kind must be one of page, template, layout, form, component'
|
|
147
|
+
}, {
|
|
148
|
+
status: 400
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
const hostId = (_url_searchParams_get1 = url.searchParams.get('hostId')) != null ? _url_searchParams_get1 : '';
|
|
152
|
+
if (!ID_CHARS.test(hostId)) return Response.json({
|
|
153
|
+
error: 'hostId is not a site id'
|
|
154
|
+
}, {
|
|
155
|
+
status: 400
|
|
156
|
+
});
|
|
157
|
+
const caller = await doorCaller(request, ((_url_searchParams_get2 = url.searchParams.get('orgId')) != null ? _url_searchParams_get2 : '').trim());
|
|
158
|
+
if (caller instanceof Response) return caller;
|
|
159
|
+
const { staff, orgId, org, member } = caller;
|
|
160
|
+
const firestore = firebaseAdmin.app().firestore();
|
|
161
|
+
const now = new Date();
|
|
162
|
+
const hidden = ()=>Response.json(aiCreateDoorView({
|
|
163
|
+
kind,
|
|
164
|
+
shown: false,
|
|
165
|
+
entitled: false,
|
|
166
|
+
purchasable: false,
|
|
167
|
+
canManageBilling: false,
|
|
168
|
+
credits: {
|
|
169
|
+
kind: 'none'
|
|
170
|
+
},
|
|
171
|
+
addonName: aiAddonName()
|
|
172
|
+
}), {
|
|
173
|
+
headers: {
|
|
174
|
+
'Cache-Control': 'no-store'
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
// The shell's rungs, in the jobs gate's order: the reader's `ai.generate`
|
|
178
|
+
// on this site, the release flag, then the site's own AI switch. Any of
|
|
179
|
+
// them draws no entry at all; staff pass the first two, as they do there.
|
|
180
|
+
if (!staff && !await memberHasPermissionOnHost(orgId, hostId, member, 'ai.generate')) return hidden();
|
|
181
|
+
if (!staff && !await isServerReleaseFlagOnForOrg('release_ai_generative', orgId)) return hidden();
|
|
182
|
+
if (await aiJobSiteRefusal({
|
|
183
|
+
firestore,
|
|
184
|
+
org,
|
|
185
|
+
hostId
|
|
186
|
+
})) return hidden();
|
|
187
|
+
const entitled = checkEntitlement(org, 'aiGenerative');
|
|
188
|
+
if (!entitled) {
|
|
189
|
+
// Its own upsell where only the add-on is missing (AGL-3601): the add-on
|
|
190
|
+
// store open to this workspace, and an add-on it can buy that grants it.
|
|
191
|
+
const storeOpen = staff || await isServerReleaseFlagOnForOrg('release_addon_store', orgId);
|
|
192
|
+
const purchasable = !staff && storeOpen && aiGenerativePurchasable(org);
|
|
193
|
+
const canManageBilling = purchasable && await memberHasPermissionOnHost(orgId, null, member, 'billing.manage');
|
|
194
|
+
return Response.json(aiCreateDoorView({
|
|
195
|
+
kind,
|
|
196
|
+
shown: true,
|
|
197
|
+
entitled: false,
|
|
198
|
+
purchasable,
|
|
199
|
+
canManageBilling,
|
|
200
|
+
credits: {
|
|
201
|
+
kind: 'none'
|
|
202
|
+
},
|
|
203
|
+
addonName: aiAddonName()
|
|
204
|
+
}), {
|
|
205
|
+
headers: {
|
|
206
|
+
'Cache-Control': 'no-store'
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
const [credits, collections, buildInFlight] = await Promise.all([
|
|
211
|
+
creditsFacts(firestore, orgId, org, now).catch((error)=>{
|
|
212
|
+
console.error('[ai-create] credits read failed', orgId, error);
|
|
213
|
+
return {
|
|
214
|
+
kind: 'none'
|
|
215
|
+
};
|
|
216
|
+
}),
|
|
217
|
+
kind === 'template' ? firestore.collection('hosts').doc(hostId).collection('collections').limit(COLLECTIONS_MAX_PER_HOST).get().then((snapshot)=>({
|
|
218
|
+
status: 'success',
|
|
219
|
+
options: aiTemplateCollectionOptions(snapshot.docs.map((doc)=>_extends({}, doc.data(), {
|
|
220
|
+
$id: doc.id
|
|
221
|
+
})))
|
|
222
|
+
})).catch(()=>({
|
|
223
|
+
status: 'error',
|
|
224
|
+
options: []
|
|
225
|
+
})) : Promise.resolve(null),
|
|
226
|
+
kind === 'page' ? listAiJobs(firestore, orgId, {
|
|
227
|
+
statuses: AI_JOB_ACTIVE_STATUSES
|
|
228
|
+
}).then((jobs)=>aiSiteBuildInFlight(jobs.map((job)=>aiJobSummary(job, now)), {
|
|
229
|
+
hostId
|
|
230
|
+
}) !== null).catch(()=>false) : Promise.resolve(false)
|
|
231
|
+
]);
|
|
232
|
+
return Response.json(aiCreateDoorView({
|
|
233
|
+
kind: kind,
|
|
234
|
+
shown: true,
|
|
235
|
+
entitled: true,
|
|
236
|
+
purchasable: false,
|
|
237
|
+
canManageBilling: false,
|
|
238
|
+
credits,
|
|
239
|
+
collections,
|
|
240
|
+
buildInFlight,
|
|
241
|
+
addonName: aiAddonName()
|
|
242
|
+
}), {
|
|
243
|
+
headers: {
|
|
244
|
+
'Cache-Control': 'no-store'
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
/** The follow view: one job of the workspace's, as the dialog that started it reads it. */ export async function GET_FOLLOW(request, { params }) {
|
|
249
|
+
var _url_searchParams_get;
|
|
250
|
+
var _summary_review;
|
|
251
|
+
const { jobId } = await params;
|
|
252
|
+
if (!ID_CHARS.test(jobId != null ? jobId : '')) return Response.json({
|
|
253
|
+
error: 'Not found'
|
|
254
|
+
}, {
|
|
255
|
+
status: 404
|
|
256
|
+
});
|
|
257
|
+
const url = new URL(request.url);
|
|
258
|
+
const gate = await aiJobsGate(request, (_url_searchParams_get = url.searchParams.get('orgId')) != null ? _url_searchParams_get : '');
|
|
259
|
+
if (gate instanceof Response) return gate;
|
|
260
|
+
const now = new Date();
|
|
261
|
+
const job = await getAiJob(gate.firestore, gate.orgId, jobId);
|
|
262
|
+
if (!job) return Response.json({
|
|
263
|
+
error: 'Not found'
|
|
264
|
+
}, {
|
|
265
|
+
status: 404
|
|
266
|
+
});
|
|
267
|
+
const summary = aiJobSummary(job, now);
|
|
268
|
+
// Confirming builds pages while another job is building on the site
|
|
269
|
+
// (AGL-3765): asked only while this one waits on its plan.
|
|
270
|
+
const askInFlight = summary.status === 'needs_review' && ((_summary_review = summary.review) == null ? void 0 : _summary_review.reason) === 'plan' && AI_PAGE_BUILDING_JOB_KINDS.includes(summary.kind);
|
|
271
|
+
const buildInFlight = askInFlight ? await listAiJobs(gate.firestore, gate.orgId, {
|
|
272
|
+
statuses: AI_JOB_ACTIVE_STATUSES
|
|
273
|
+
}).then((jobs)=>aiSiteBuildInFlight(jobs.map((one)=>aiJobSummary(one, now)), {
|
|
274
|
+
hostId: summary.hostId,
|
|
275
|
+
exceptJobId: summary.id
|
|
276
|
+
}) !== null).catch(()=>false) : false;
|
|
277
|
+
return Response.json({
|
|
278
|
+
job: summary,
|
|
279
|
+
view: aiJobFollowView(summary, {
|
|
280
|
+
// Console addresses name the workspace by its slug, as every link the apps resolve does.
|
|
281
|
+
orgSlug: typeof gate.org.slug === 'string' ? gate.org.slug : null,
|
|
282
|
+
staff: gate.staff,
|
|
283
|
+
buildInFlight,
|
|
284
|
+
now: now.getTime()
|
|
285
|
+
})
|
|
286
|
+
}, {
|
|
287
|
+
status: 200,
|
|
288
|
+
headers: {
|
|
289
|
+
'Cache-Control': 'no-store'
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
export const dynamic = 'force-dynamic';
|
|
294
|
+
|
|
295
|
+
//# sourceMappingURL=ai-create-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/server/ai-create-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// lockdown-423: via libs/plugins/ai/src/lib/server/ai-jobs-gate.ts\n// The follow view climbs `aiJobsGate`, whose lockdown rung is the verdict. The\n// door answers what a list draws and starts nothing: a locked workspace's\n// start is refused by the jobs route's own ladder, in its own words.\n\nimport type { DecodedIdToken } from 'firebase-admin/auth'\nimport { checkEntitlement } from '@aglyn/aglyn/server'\nimport { COLLECTIONS_MAX_PER_HOST } from '@aglyn/aglyn/app-utils/collection-entries'\nimport {\n aiAddonName,\n isUncappedPlanComp,\n orgSubscriptionState,\n resolveEffectivePlan,\n} from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { listPluginSeatAddons } from '@aglyn/aglyn/plugin-manager/plugin-entitlements'\nimport type { AglynOrganization, AglynOrgMember } from '@aglyn/aglyn/foundation/definitions/organization.types'\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 { getOrgForUser, memberHasPermissionOnHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport { isServerReleaseFlagOnForOrg } from '@aglyn/tenant-data-admin/server/release-flags'\nimport { aiJobSiteRefusal } from '../jobs/ai-job-admission'\nimport { aiJobSummary, getAiJob, listAiJobs } from '../jobs/ai-jobs'\nimport {\n aiCreateDoorView,\n aiJobFollowView,\n isAiCreateKind,\n type AiCreateCreditsFacts,\n type AiCreateKind,\n} from '../model/ai-create-view'\nimport { AI_JOB_ACTIVE_STATUSES, AI_PAGE_BUILDING_JOB_KINDS, aiSiteBuildInFlight } from '../model/ai-job-activity'\nimport { aiTemplateCollectionOptions } from '../model/ai-template-collections'\nimport { assistSpendAfterReturnsUsd, ASSIST_RETURNED_USD_FIELD } from '../usage/assist-credit-returns'\nimport { assistBandRefuses, publicAssistCredits, resolveAssistBudgetUsd } from '../usage/assist-credits'\nimport { readFreeAiCreditsLeft } from '../usage/free-ai-credits-left'\nimport { aiJobsGate } from './ai-jobs-gate'\n\n/**\n * `GET /api/ai/create?orgId&hostId&kind` — the door one list's \"Create with\n * AI\" reads in the apps (AGL-3854), and `GET /api/ai/create/:jobId?orgId` —\n * the job it started, as the dialog's follow card reads it.\n *\n * The console draws each entry from its shell's gates and its own components\n * (`ai-describe-button`, `ai-brief-dialog`, `ai-job-follow`); the apps have\n * neither, so these answer the same verdicts and the same words as data\n * (`model/ai-create-view.ts`). Both only read. Starting, confirming and\n * canceling go through the jobs routes the console calls, whose ladder decides\n * every request exactly as it does for the console.\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\n/** Every rung the console's shell climbs before it draws the entry, in the jobs gate's order. */\ninterface DoorCaller {\n decoded: DecodedIdToken\n staff: boolean\n orgId: string\n org: Partial<AglynOrganization>\n member: AglynOrgMember | null\n}\n\nasync function doorCaller(request: Request, orgId: string): Promise<Response | DoorCaller> {\n const idToken = bearerToken(request)\n if (!idToken) return Response.json({ error: 'Unauthenticated' }, { status: 401 })\n let decoded: DecodedIdToken\n try {\n decoded = await firebaseAdmin.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 if (!orgId) return Response.json({ error: 'Open a workspace before using this feature' }, { status: 400 })\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 return {\n decoded,\n staff: decoded['staff'] === true,\n orgId,\n org: resolved.org ?? {},\n member: resolved.member ?? null,\n }\n}\n\n/**\n * Whether an add-on this workspace can buy grants `aiGenerative`: the\n * console's `entitlementPurchasable`, over the add-ons the AI plugin declares\n * on the server.\n */\nexport function aiGenerativePurchasable(org: Partial<AglynOrganization>): boolean {\n const billing = org as Parameters<typeof checkEntitlement>[0]\n if (orgSubscriptionState(billing as never) !== 'live') return false\n const plan = resolveEffectivePlan(billing as never)\n return listPluginSeatAddons().some(\n (addon) =>\n addon.features?.includes('aiGenerative') && (!addon.quota || (addon.quota.perUnitByPlan[plan] ?? 0) > 0),\n )\n}\n\n/** What is left of the month's credits, as the AI credits meter reads it. */\nasync function creditsFacts(\n firestore: FirebaseFirestore.Firestore,\n orgId: string,\n org: Partial<AglynOrganization>,\n now: Date,\n): Promise<AiCreateCreditsFacts> {\n if (resolveEffectivePlan(org as never) === 'free') {\n const left = await readFreeAiCreditsLeft(firestore, { orgId, org: org as object, now })\n return left ? { kind: 'free', left } : { kind: 'none' }\n }\n const budgetUsd = resolveAssistBudgetUsd(org as never)\n if (budgetUsd === null) return isUncappedPlanComp(org as never) ? { kind: 'unlimited' } : { kind: 'none' }\n const usage = await firestore\n .collection('orgs')\n .doc(orgId)\n .collection('assistUsage')\n .doc(now.toISOString().slice(0, 7))\n .get()\n const credits = publicAssistCredits(\n assistSpendAfterReturnsUsd(usage.get('estCostUsd'), usage.get(ASSIST_RETURNED_USD_FIELD)),\n budgetUsd,\n )\n if (credits.limit === null || credits.remaining === null) return { kind: 'none' }\n return {\n kind: 'band',\n used: credits.used,\n limit: credits.limit,\n remaining: credits.remaining,\n stopsAtBand: assistBandRefuses(org as never),\n }\n}\n\nconst ID_CHARS = /^[A-Za-z0-9_-]{1,100}$/\n\n/** The door: whether a list draws Create with AI, as what, and every word its brief says. */\nexport async function GET(request: Request): Promise<Response> {\n const url = new URL(request.url)\n const kind = url.searchParams.get('kind') ?? ''\n if (!isAiCreateKind(kind)) {\n return Response.json({ error: 'kind must be one of page, template, layout, form, component' }, { status: 400 })\n }\n const hostId = url.searchParams.get('hostId') ?? ''\n if (!ID_CHARS.test(hostId)) return Response.json({ error: 'hostId is not a site id' }, { status: 400 })\n const caller = await doorCaller(request, (url.searchParams.get('orgId') ?? '').trim())\n if (caller instanceof Response) return caller\n const { staff, orgId, org, member } = caller\n const firestore = firebaseAdmin.app().firestore()\n const now = new Date()\n const hidden = () =>\n Response.json(\n aiCreateDoorView({\n kind,\n shown: false,\n entitled: false,\n purchasable: false,\n canManageBilling: false,\n credits: { kind: 'none' },\n addonName: aiAddonName(),\n }),\n { headers: { 'Cache-Control': 'no-store' } },\n )\n\n // The shell's rungs, in the jobs gate's order: the reader's `ai.generate`\n // on this site, the release flag, then the site's own AI switch. Any of\n // them draws no entry at all; staff pass the first two, as they do there.\n if (!staff && !(await memberHasPermissionOnHost(orgId, hostId, member, 'ai.generate'))) return hidden()\n if (!staff && !(await isServerReleaseFlagOnForOrg('release_ai_generative', orgId))) return hidden()\n if (await aiJobSiteRefusal({ firestore, org, hostId })) return hidden()\n\n const entitled = checkEntitlement(org as never, 'aiGenerative')\n if (!entitled) {\n // Its own upsell where only the add-on is missing (AGL-3601): the add-on\n // store open to this workspace, and an add-on it can buy that grants it.\n const storeOpen = staff || (await isServerReleaseFlagOnForOrg('release_addon_store', orgId))\n const purchasable = !staff && storeOpen && aiGenerativePurchasable(org)\n const canManageBilling = purchasable && (await memberHasPermissionOnHost(orgId, null, member, 'billing.manage'))\n return Response.json(\n aiCreateDoorView({\n kind,\n shown: true,\n entitled: false,\n purchasable,\n canManageBilling,\n credits: { kind: 'none' },\n addonName: aiAddonName(),\n }),\n { headers: { 'Cache-Control': 'no-store' } },\n )\n }\n\n const [credits, collections, buildInFlight] = await Promise.all([\n creditsFacts(firestore, orgId, org, now).catch((error) => {\n console.error('[ai-create] credits read failed', orgId, error)\n return { kind: 'none' } as const\n }),\n kind === 'template'\n ? firestore\n .collection('hosts')\n .doc(hostId)\n .collection('collections')\n .limit(COLLECTIONS_MAX_PER_HOST)\n .get()\n .then((snapshot) => ({\n status: 'success' as const,\n options: aiTemplateCollectionOptions(\n snapshot.docs.map((doc) => ({ ...(doc.data() as Record<string, unknown>), $id: doc.id })),\n ),\n }))\n .catch(() => ({ status: 'error' as const, options: [] }))\n : Promise.resolve(null),\n kind === 'page'\n ? listAiJobs(firestore, orgId, { statuses: AI_JOB_ACTIVE_STATUSES })\n .then((jobs) => aiSiteBuildInFlight(jobs.map((job) => aiJobSummary(job, now)), { hostId }) !== null)\n .catch(() => false)\n : Promise.resolve(false),\n ])\n return Response.json(\n aiCreateDoorView({\n kind: kind as AiCreateKind,\n shown: true,\n entitled: true,\n purchasable: false,\n canManageBilling: false,\n credits,\n collections,\n buildInFlight,\n addonName: aiAddonName(),\n }),\n { headers: { 'Cache-Control': 'no-store' } },\n )\n}\n\n/** The follow view: one job of the workspace's, as the dialog that started it reads it. */\nexport async function GET_FOLLOW(request: Request, { params }: { params: Promise<{ jobId: string }> }): Promise<Response> {\n const { jobId } = await params\n if (!ID_CHARS.test(jobId ?? '')) return Response.json({ error: 'Not found' }, { status: 404 })\n const url = new URL(request.url)\n const gate = await aiJobsGate(request, url.searchParams.get('orgId') ?? '')\n if (gate instanceof Response) return gate\n const now = new Date()\n const job = await getAiJob(gate.firestore, gate.orgId, jobId)\n if (!job) return Response.json({ error: 'Not found' }, { status: 404 })\n const summary = aiJobSummary(job, now)\n // Confirming builds pages while another job is building on the site\n // (AGL-3765): asked only while this one waits on its plan.\n const askInFlight =\n summary.status === 'needs_review' &&\n summary.review?.reason === 'plan' &&\n AI_PAGE_BUILDING_JOB_KINDS.includes(summary.kind)\n const buildInFlight = askInFlight\n ? await listAiJobs(gate.firestore, gate.orgId, { statuses: AI_JOB_ACTIVE_STATUSES })\n .then(\n (jobs) =>\n aiSiteBuildInFlight(\n jobs.map((one) => aiJobSummary(one, now)),\n { hostId: summary.hostId, exceptJobId: summary.id },\n ) !== null,\n )\n .catch(() => false)\n : false\n return Response.json(\n {\n job: summary,\n view: aiJobFollowView(summary, {\n // Console addresses name the workspace by its slug, as every link the apps resolve does.\n orgSlug: typeof gate.org.slug === 'string' ? gate.org.slug : null,\n staff: gate.staff,\n buildInFlight,\n now: now.getTime(),\n }),\n },\n { status: 200, headers: { 'Cache-Control': 'no-store' } },\n )\n}\n\nexport const dynamic = 'force-dynamic'\n"],"names":["checkEntitlement","COLLECTIONS_MAX_PER_HOST","aiAddonName","isUncappedPlanComp","orgSubscriptionState","resolveEffectivePlan","listPluginSeatAddons","emailUnverifiedResponse","firebaseAdmin","isImpersonationSession","invalidIdTokenResponse","getOrgForUser","memberHasPermissionOnHost","isServerReleaseFlagOnForOrg","aiJobSiteRefusal","aiJobSummary","getAiJob","listAiJobs","aiCreateDoorView","aiJobFollowView","isAiCreateKind","AI_JOB_ACTIVE_STATUSES","AI_PAGE_BUILDING_JOB_KINDS","aiSiteBuildInFlight","aiTemplateCollectionOptions","assistSpendAfterReturnsUsd","ASSIST_RETURNED_USD_FIELD","assistBandRefuses","publicAssistCredits","resolveAssistBudgetUsd","readFreeAiCreditsLeft","aiJobsGate","bearerToken","request","authorization","headers","get","startsWith","slice","length","undefined","doorCaller","orgId","resolved","idToken","Response","json","error","status","decoded","app","auth","verifyIdToken","unauthenticated","email_verified","uid","staff","org","member","aiGenerativePurchasable","billing","plan","some","addon","features","includes","quota","perUnitByPlan","creditsFacts","firestore","now","left","kind","budgetUsd","usage","collection","doc","toISOString","credits","limit","remaining","used","stopsAtBand","ID_CHARS","GET","url","URL","searchParams","hostId","test","caller","trim","Date","hidden","shown","entitled","purchasable","canManageBilling","addonName","storeOpen","collections","buildInFlight","Promise","all","catch","console","then","snapshot","options","docs","map","data","$id","id","resolve","statuses","jobs","job","GET_FOLLOW","params","summary","jobId","gate","askInFlight","review","reason","one","exceptJobId","view","orgSlug","slug","getTime","dynamic"],"mappings":"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"}
|
package/src/lib/server.js
CHANGED
|
@@ -52,6 +52,7 @@ import { POST as cancelAiJob } from "./server/ai-jobs-cancel.js";
|
|
|
52
52
|
import { GET as aiJobEvents } from "./server/ai-jobs-events-route.js";
|
|
53
53
|
import { POST as createAiSiteBatch } from "./server/ai-jobs-batch.js";
|
|
54
54
|
import { GET as listAiJobs, POST as createAiJob } from "./server/ai-jobs-route.js";
|
|
55
|
+
import { GET as aiCreateDoor, GET_FOLLOW as aiCreateFollow } from "./server/ai-create-route.js";
|
|
55
56
|
import { POST as resumeAiJob } from "./server/ai-jobs-resume.js";
|
|
56
57
|
import { POST as aiGenerateComponent } from "./server/ai-generate-component.js";
|
|
57
58
|
import { POST as createAiMediaImages } from "./server/ai-media-image.js";
|
|
@@ -241,6 +242,19 @@ export * from "./runtime/ai-runtime.js";
|
|
|
241
242
|
})
|
|
242
243
|
})
|
|
243
244
|
});
|
|
245
|
+
// Create with AI in the apps (AGL-3854): whether a list draws the entry and
|
|
246
|
+
// every word its brief says, and the job it started as the dialog's follow
|
|
247
|
+
// card reads it. Both read; the jobs doors above start, confirm and cancel.
|
|
248
|
+
registerPluginApiRoute('ai/create', {
|
|
249
|
+
web: aiCreateDoor
|
|
250
|
+
});
|
|
251
|
+
registerPluginApiRoute('ai/create/:jobId', {
|
|
252
|
+
web: (request, context)=>aiCreateFollow(request, {
|
|
253
|
+
params: Promise.resolve({
|
|
254
|
+
jobId: String(context.params['jobId'])
|
|
255
|
+
})
|
|
256
|
+
})
|
|
257
|
+
});
|
|
244
258
|
registerPluginApiRoute('ai/jobs/:jobId/events', {
|
|
245
259
|
web: (request, context)=>aiJobEvents(request, {
|
|
246
260
|
params: Promise.resolve({
|
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 { 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 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","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 { 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":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,sBAAqB;AAC5D,SAASC,sBAAsB,QAAQ,oBAAgB;AACvD,SAASC,iBAAiB,QAAQ,yBAAqB;AACvD,SAASC,iBAAiB,QAAQ,6BAAyB;AAC3D,SAASC,mBAAmB,QAAQ,0BAAsB;AAC1D,SAASC,oBAAoB,QAAQ,2BAAuB;AAC5D,SAASC,sBAAsB,QAAQ,kCAA8B;AACrE,SAASC,gBAAgB,QAAQ,4BAAwB;AACzD,SAASC,mBAAmB,QAAQ,+BAA2B;AAC/D,SAASC,qBAAqB,QAAQ,iCAA6B;AACnE,SAASC,uBAAuB,QAAQ,mCAA+B;AACvE,SAASC,iBAAiB,QAAQ,6BAAyB;AAC3D,SAASC,iBAAiB,QAAQ,6BAAyB;AAC3D,SAASC,qBAAqB,QAAQ,iCAA6B;AACnE,SAASC,kBAAkB,QAAQ,8BAA0B;AAC7D,SAASC,qBAAqB,QAAQ,iCAA6B;AACnE,SAASC,iBAAiB,QAAQ,6BAAyB;AAC3D,SAASC,kBAAkB,QAAQ,8BAA0B;AAC7D,SAASC,2BAA2B,QAAQ,kCAA8B;AAC1E,SAASC,qBAAqB,QAAQ,iCAA6B;AACnE,SAASC,kBAAkB,QAAQ,8BAA0B;AAC7D,SAASC,iBAAiB,QAAQ,6BAAyB;AAC3D,SAASC,uBAAuB,QAAQ,mCAA+B;AACvE,SAASC,sBAAsB,QAAQ,kCAA8B;AACrE,SAASC,qBAAqB,QAAQ,iCAA6B;AACnE,SAASC,kBAAkB,QAAQ,8BAA0B;AAC7D,SAASC,qBAAqB,QAAQ,iCAA6B;AACnE,SAASC,oBAAoB,QAAQ,gCAA4B;AACjE,SAASC,uBAAuB,QAAQ,kCAA8B;AACtE,SAASC,uBAAuB,QAAQ,qCAAiC;AACzE,SAASC,aAAa,QAAQ,iDAAgD;AAC9E,SAASC,2BAA2B,QAAQ,0BAAsB;AAClE,SAASC,eAAe,QAAQ,wBAAoB;AACpD,SAASC,OAAOC,WAAW,QAAQ,4BAAwB;AAC3D,SAASC,QAAQC,aAAa,QAAQ,iCAA6B;AACnE,SAASD,QAAQE,WAAW,QAAQ,6BAAyB;AAC7D,SAASJ,OAAOK,WAAW,QAAQ,mCAA+B;AAClE,SAASH,QAAQI,iBAAiB,QAAQ,4BAAwB;AAClE,SAASN,OAAOO,UAAU,EAAEL,QAAQM,WAAW,QAAQ,4BAAwB;AAC/E,SAASR,OAAOS,YAAY,EAAEC,cAAcC,cAAc,QAAQ,8BAA0B;AAC5F,SAAST,QAAQU,WAAW,QAAQ,6BAAyB;AAC7D,SAASV,QAAQW,mBAAmB,QAAQ,oCAAgC;AAC5E,SAASX,QAAQY,mBAAmB,QAAQ,6BAAyB;AACrE,SAASZ,QAAQa,eAAe,QAAQ,2BAAuB;AAC/D,SAASb,QAAQc,0BAA0B,QAAQ,qCAAiC;AACpF,SAASd,QAAQe,UAAU,QAAQ,0BAAsB;AACzD,SAASf,QAAQgB,iBAAiB,QAAQ,kCAA8B;AACxE,SAAShB,QAAQiB,cAAc,QAAQ,8BAA0B;AACjE,SAASC,SAASC,iBAAiB,QAAQ,kCAA8B;AACzE,SAASrB,OAAOsB,UAAU,QAAQ,2BAAuB;AACzD,SAAStB,OAAOuB,oBAAoB,QAAQ,qCAAiC;AAC7E,SAASvB,OAAOwB,gBAAgB,QAAQ,kCAA8B;AACtE,SAASxB,OAAOyB,cAAc,QAAQ,+BAA2B;AACjE,SAASzB,OAAO0B,WAAW,QAAQ,4BAAwB;AAC3D,SAASxB,QAAQyB,cAAc,QAAQ,+BAA2B;AAClE,SAASzB,QAAQ0B,cAAc,QAAQ,+BAA2B;AAClE,SAAS5B,OAAO6B,OAAO,QAAQ,uBAAmB;AAClD,SAAS7B,OAAO8B,YAAY,QAAQ,4BAAwB;AAC5D,SAAS9B,OAAO+B,eAAe,QAAQ,gCAA4B;AACnE,SAAS7B,QAAQ8B,mBAAmB,QAAQ,sCAAkC;AAC9E,SAASC,uBAAuB,QAAQ,kCAA8B;AACtE,SAASjC,OAAOkC,QAAQ,QAAQ,wBAAoB;AACpD,SAASlC,OAAOmC,cAAc,QAAQ,8BAA0B;AAChE,SAASjC,QAAQkC,cAAc,QAAQ,8BAA0B;AAEjE,cAAc,0BAAsB;AACpC,cAAc,yBAAqB;AACnC,cAAc,0BAAsB;AACpC,cAAc,yBAAqB;AACnC,cAAc,0BAAsB;AAEpC;;;;CAIC,GACD,MAAMC,8BAA8BvC;AAEpC;;;;;;;;;;;;CAYC,GACD,SAASwC;IACP,0EAA0E;IAC1E,2DAA2D;IAC3DnE;IACA,uEAAuE;IACvE,oDAAoD;IACpDC;IACA,0DAA0D;IAC1DF;IACA,0DAA0D;IAC1DG;IACAE;IACAC;IACA,oBAAoB;IACpBC;IACAC;IACA,wDAAwD;IACxDC;IACA,oEAAoE;IACpE,uDAAuD;IACvDE;IACAC;IACA,0EAA0E;IAC1E,iEAAiE;IACjEI;IACA,0EAA0E;IAC1E,qEAAqE;IACrEC;IACA,yEAAyE;IACzE,2EAA2E;IAC3EC;IACA,0EAA0E;IAC1E,oDAAoD;IACpDC;IACA,yEAAyE;IACzE,2DAA2D;IAC3DC;IACA,oEAAoE;IACpE,mEAAmE;IACnEC;IACA,uEAAuE;IACvE,8DAA8D;IAC9DC;IACA,sEAAsE;IACtE,+BAA+B;IAC/BC;IACA,wEAAwE;IACxE,uEAAuE;IACvEb;IACA,uEAAuE;IACvE,cAAc;IACdG;IACA,kEAAkE;IAClE,yEAAyE;IACzE,wEAAwE;IACxE,kEAAkE;IAClEE;IACAD;IACA,wEAAwE;IACxE,2EAA2E;IAC3E,sEAAsE;IACtE,2EAA2E;IAC3EW,wBAAwB,IAAME,cAAc0C,GAAG,GAAGC,SAAS;IAC3D9C;IACA,yEAAyE;IACzE,wEAAwE;IACxEpB;AACF;AAEA;;;;;;;;;CASC,GACD,OAAO,SAASmE;IACdzE;IACAqE;IACAC;IACA,mEAAmE;IACnE,qEAAqE;IACrE,yDAAyD;IACzD1C;IACA7B,uBAAuB,eAAe;QAAE2E,KAAKzB;IAAW;IACxDlD,uBAAuB,mBAAmB;QAAE2E,KAAKvB;IAAe;IAChE,sEAAsE;IACtE,+DAA+D;IAC/DpD,uBAAuB,uBAAuB;QAAE2E,KAAKxB;IAAkB;IACvE,0EAA0E;IAC1E,yDAAyD;IACzDnD,uBAAuB,uBAAuB;QAAE2E,KAAKrB;IAAkB;IACvEtD,uBAAuB,aAAagC;IACpChC,uBAAuB,WAAW;QAChC2E,KAAK,CAACC,UAAaA,QAAQC,MAAM,KAAK,QAAQrC,WAAWoC,WAAWnC,YAAYmC;IAClF;IACA,yEAAyE;IACzE,0EAA0E;IAC1E5E,uBAAuB,iBAAiB;QAAE2E,KAAKpC;IAAkB;IACjEvC,uBAAuB,yBAAyB;QAC9C2E,KAAK,CAACC,SAASE,UACbzC,YAAYuC,SAAS;gBAAEG,QAAQC,QAAQC,OAAO,CAAC;oBAAEC,OAAOC,OAAOL,QAAQC,MAAM,CAAC,QAAQ;gBAAE;YAAG;IAC/F;IACA/E,uBAAuB,yBAAyB;QAC9C2E,KAAK,CAACC,SAASE,UACbjC,YAAY+B,SAAS;gBAAEG,QAAQC,QAAQC,OAAO,CAAC;oBAAEC,OAAOC,OAAOL,QAAQC,MAAM,CAAC,QAAQ;gBAAE;YAAG;IAC/F;IACA,yEAAyE;IACzE,yEAAyE;IACzE,WAAW;IACX/E,uBAAuB,sBAAsB;QAC3C2E,KAAK,CAACC,SAASE,UACbd,gBAAgBY,SAAS;gBAAEG,QAAQC,QAAQC,OAAO,CAAC;oBAAEC,OAAOC,OAAOL,QAAQC,MAAM,CAAC,QAAQ;gBAAE;YAAG;IACnG;IACA,wEAAwE;IACxE,8DAA8D;IAC9D/E,uBAAuB,iBAAiB;QACtC2E,KAAK,CAACC,SAASE,UACb5C,YAAY0C,SAAS;gBAAEG,QAAQC,QAAQC,OAAO,CAAC;oBAAEC,OAAOC,OAAOL,QAAQC,MAAM,CAAC,QAAQ;gBAAE;YAAG;IAC/F;IACA,4EAA4E;IAC5E,2EAA2E;IAC3E,4EAA4E;IAC5E/E,uBAAuB,aAAa;QAAE2E,KAAKjC;IAAa;IACxD1C,uBAAuB,oBAAoB;QACzC2E,KAAK,CAACC,SAASE,UACblC,eAAegC,SAAS;gBAAEG,QAAQC,QAAQC,OAAO,CAAC;oBAAEC,OAAOC,OAAOL,QAAQC,MAAM,CAAC,QAAQ;gBAAE;YAAG;IAClG;IACA/E,uBAAuB,yBAAyB;QAC9C2E,KAAK,CAACC,SAASE,UACbxC,YAAYsC,SAAS;gBAAEG,QAAQC,QAAQC,OAAO,CAAC;oBAAEC,OAAOC,OAAOL,QAAQC,MAAM,CAAC,QAAQ;gBAAE;YAAG;IAC/F;IACA,yEAAyE;IACzE,2EAA2E;IAC3E,iCAAiC;IACjC/E,uBAAuBE,mBAAmB;QAAEyE,KAAKvC;IAAc;IAC/D,2EAA2E;IAC3E,2EAA2E;IAC3E,uEAAuE;IACvE,yEAAyE;IACzE,0EAA0E;IAC1E,wEAAwE;IACxE,+DAA+D;IAC/DpC,uBAAuBkE,yBAAyB;QAAES,KAAKV;IAAoB,GAAG;QAAEmB,SAAS;IAAK;IAC9F,kEAAkE;IAClE,mEAAmE;IACnEpF,uBAAuB,gBAAgB;QAAE2E,KAAK3B;IAAgB;IAC9D,yEAAyE;IACzE,yEAAyE;IACzE,+DAA+D;IAC/DhD,uBAAuB,2BAA2B;QAAE2E,KAAK1B;IAA2B;IACpF,sEAAsE;IACtE,0EAA0E;IAC1E,qDAAqD;IACrDjD,uBAAuB,yBAAyB;QAAE2E,KAAK7B;IAAoB;IAC3E,mEAAmE;IACnE,wEAAwE;IACxE,yEAAyE;IACzE9C,uBAAuB,mBAAmB;QAAE2E,KAAK5B;IAAoB;IACrE/C,uBAAuB,sBAAsB;QAAE2E,KAAKP;IAAe;IACnEpE,uBAAuB,sBAAsB;QAAE2E,KAAKN;IAAe;IACnErE,uBAAuB,YAAY;QAAE2E,KAAKb;IAAQ;IAClD,wEAAwE;IACxE,gEAAgE;IAChE9D,uBAAuB,iBAAiB;QAAE2E,KAAKZ;IAAa;IAC5D/D,uBAAuB,aAAa;QAAE2E,KAAKR;IAAS;IACpD,wEAAwE;IACxE,oEAAoE;IACpE,+CAA+C;IAC/CnE,uBAAuB,gBAAgB;QAAE2E,KAAKpB;IAAW;IACzDvD,uBAAuB,uBAAuB;QAAE2E,KAAKlB;IAAiB;IACtEzD,uBAAuB,iBAAiB;QAAE2E,KAAKhB;IAAY;IAC3D3D,uBAAuB,oBAAoB;QAAE2E,KAAKjB;IAAe;IACjE,4EAA4E;IAC5E,sDAAsD;IACtD1D,uBAAuB,0BAA0B;QAAE2E,KAAKnB;IAAqB;IAC7E,wEAAwE;IACxE,qEAAqE;IACrE,uEAAuE;IACvExD,uBAAuB,oBAAoB;QAAE2E,KAAKf;IAAe;IACjE,uEAAuE;IACvE,0EAA0E;IAC1E,mDAAmD;IACnD5D,uBAAuB,oBAAoB;QAAE2E,KAAKd;IAAe;AACnE"}
|