@builder.io/ai-utils 0.91.1 → 0.92.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/codegen.d.ts +6 -0
- package/src/codegen.js +3 -0
- package/src/completion.d.ts +4 -0
- package/src/organization.d.ts +10 -0
- package/src/projects.d.ts +1509 -1
- package/src/projects.js +203 -1
- package/src/projects.test.js +12 -1
package/src/projects.js
CHANGED
|
@@ -25,6 +25,16 @@ export function getGitProviderFromUrl(url) {
|
|
|
25
25
|
return "unknown";
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
export const GIT_PROVIDERS = [
|
|
29
|
+
"github",
|
|
30
|
+
"selfHostedGithub",
|
|
31
|
+
"bitbucket",
|
|
32
|
+
"gitlab",
|
|
33
|
+
"azure",
|
|
34
|
+
"internalHost",
|
|
35
|
+
"custom",
|
|
36
|
+
];
|
|
37
|
+
export const GitProviderSchema = z.enum(GIT_PROVIDERS);
|
|
28
38
|
export const EXAMPLE_REPOS = [
|
|
29
39
|
"steve8708/mui-vite",
|
|
30
40
|
"steve8708/carbon-vite",
|
|
@@ -970,6 +980,14 @@ export const ProjectHostingBuildConfigSchema = z.object({
|
|
|
970
980
|
buildOutputDir: z.string().optional(),
|
|
971
981
|
nodeVersion: z.string().optional(),
|
|
972
982
|
});
|
|
983
|
+
/**
|
|
984
|
+
* Who pays for AI usage on a project's published site.
|
|
985
|
+
*
|
|
986
|
+
* `undefined` is reserved for headless publishes that never reach
|
|
987
|
+
* `POST /projects/hosting/ai-usage`: it is the signal that makes the publish
|
|
988
|
+
* step fire, so every interactive path writes a value, `"skip"` included.
|
|
989
|
+
*/
|
|
990
|
+
export const AiPaymentModeSchema = z.enum(["builder-credits", "byok", "skip"]);
|
|
973
991
|
/**
|
|
974
992
|
* Hosting configuration for a project, stored at `projects/{projectId}.hosting`.
|
|
975
993
|
*/
|
|
@@ -1015,6 +1033,8 @@ export const ProjectHostingSchema = z.object({
|
|
|
1015
1033
|
lastDeployId: z.string().optional(),
|
|
1016
1034
|
lastDeployAt: z.number().optional(),
|
|
1017
1035
|
lastDeployStatus: DeployStatusSchema.optional(),
|
|
1036
|
+
/** Kubernetes branch route currently serving; absent means the default route. */
|
|
1037
|
+
publishedBranchId: z.string().optional(),
|
|
1018
1038
|
/** Git commit currently live at the project's hosting URL (set on go-live). */
|
|
1019
1039
|
publishedCommit: z.string().optional(),
|
|
1020
1040
|
autoDeploy: z
|
|
@@ -1042,6 +1062,24 @@ export const ProjectHostingSchema = z.object({
|
|
|
1042
1062
|
domainIds: z.array(z.string()).optional().meta({
|
|
1043
1063
|
description: "Doc keys (normalized domains) in `hosting-domains` attached to this project. The domain docs are the source of truth; this is only the back-pointer.",
|
|
1044
1064
|
}),
|
|
1065
|
+
/**
|
|
1066
|
+
* Who pays for AI usage on the published site. Written only by
|
|
1067
|
+
* `POST /projects/hosting/ai-usage`, which gates on `fusionHostingPublish`;
|
|
1068
|
+
* deliberately kept out of {@link PROJECT_UPDATABLE_HOSTING_FIELDS_MASK} so
|
|
1069
|
+
* the generic `PATCH` (gated on `modifyProjectSettings`) cannot write it.
|
|
1070
|
+
*/
|
|
1071
|
+
aiPaymentMode: AiPaymentModeSchema.optional().meta({
|
|
1072
|
+
description: "Who pays for AI usage on the published site. Absent means the first-publish step has never run for this project.",
|
|
1073
|
+
}),
|
|
1074
|
+
/**
|
|
1075
|
+
* When a hosting field that is baked into the deployed site last changed
|
|
1076
|
+
* (env vars, build config, AI payment mode). Compared against
|
|
1077
|
+
* `lastDeployAt` to decide whether the live site is stale: server-side so a
|
|
1078
|
+
* change made in one browser session is visible in every other.
|
|
1079
|
+
*/
|
|
1080
|
+
configUpdatedAt: z.number().optional().meta({
|
|
1081
|
+
description: "Epoch ms of the last change to a hosting field that requires a redeploy. Compare against `lastDeployAt`.",
|
|
1082
|
+
}),
|
|
1045
1083
|
});
|
|
1046
1084
|
/**
|
|
1047
1085
|
* Mask of the hosting sub-fields a client may write via `PATCH`. Everything
|
|
@@ -1052,7 +1090,7 @@ export const ProjectHostingSchema = z.object({
|
|
|
1052
1090
|
* new field added to `ProjectHostingSchema` must be opted *in* to be writable,
|
|
1053
1091
|
* so the default for anything we add later is "not client-writable".
|
|
1054
1092
|
*/
|
|
1055
|
-
const PROJECT_UPDATABLE_HOSTING_FIELDS_MASK = {
|
|
1093
|
+
export const PROJECT_UPDATABLE_HOSTING_FIELDS_MASK = {
|
|
1056
1094
|
enabled: true,
|
|
1057
1095
|
buildConfig: true,
|
|
1058
1096
|
environment: true,
|
|
@@ -1184,6 +1222,18 @@ export const DeploySchema = z.object({
|
|
|
1184
1222
|
devBranchId: z.string().optional().meta({
|
|
1185
1223
|
description: "Fusion dev branch's id (unset if we are deploying project default branch)",
|
|
1186
1224
|
}),
|
|
1225
|
+
supersededRouteBranchId: z.string().nullable().optional().meta({
|
|
1226
|
+
description: "Prior published route awaiting cleanup after a branch switch; null identifies the default route.",
|
|
1227
|
+
}),
|
|
1228
|
+
supersededRouteResourceVersions: z
|
|
1229
|
+
.object({
|
|
1230
|
+
route: z.string().optional(),
|
|
1231
|
+
handshake: z.string().optional(),
|
|
1232
|
+
})
|
|
1233
|
+
.optional()
|
|
1234
|
+
.meta({
|
|
1235
|
+
description: "Kubernetes versions that fence deletion of the superseded route resources.",
|
|
1236
|
+
}),
|
|
1187
1237
|
status: DeployStatusSchema,
|
|
1188
1238
|
netlifyDeployId: z.string().optional(),
|
|
1189
1239
|
url: z.string().optional(),
|
|
@@ -1461,6 +1511,92 @@ export const DeleteHostingRequestSchema = z.object({
|
|
|
1461
1511
|
export const DeleteHostingResponseSchema = z.object({
|
|
1462
1512
|
success: z.literal(true),
|
|
1463
1513
|
});
|
|
1514
|
+
// ---------------------------------------------------------------------------
|
|
1515
|
+
// AI usage (who pays for AI on the published site)
|
|
1516
|
+
// ---------------------------------------------------------------------------
|
|
1517
|
+
/** Query for `GET /projects/hosting/ai-usage`. */
|
|
1518
|
+
export const GetHostingAiUsageRequestSchema = z.object({
|
|
1519
|
+
projectId: z.string().min(1),
|
|
1520
|
+
});
|
|
1521
|
+
/**
|
|
1522
|
+
* Status of the published site's gateway token. Recomputed from the
|
|
1523
|
+
* authoritative PAT doc on every read; the plaintext token is never included.
|
|
1524
|
+
*/
|
|
1525
|
+
export const HostingAiUsageTokenStatusSchema = z.object({
|
|
1526
|
+
active: z.boolean(),
|
|
1527
|
+
lastUsed: z.number().optional(),
|
|
1528
|
+
createdBy: z.string().optional(),
|
|
1529
|
+
});
|
|
1530
|
+
export const GetHostingAiUsageResponseSchema = z.object({
|
|
1531
|
+
/**
|
|
1532
|
+
* Whether the project has ever been deployed. Server-derived from
|
|
1533
|
+
* `hosting.netlifySiteId`, which the client can never see.
|
|
1534
|
+
*/
|
|
1535
|
+
hasEverPublished: z.boolean(),
|
|
1536
|
+
aiPaymentMode: AiPaymentModeSchema.optional(),
|
|
1537
|
+
/**
|
|
1538
|
+
* Whether `"builder-credits"` can be honoured for this project: the LD flag
|
|
1539
|
+
* AND a `coreVersion` floor at or above the minimum supporting release.
|
|
1540
|
+
* Per-project, because core is pinned in the project's own lockfile.
|
|
1541
|
+
*/
|
|
1542
|
+
coreSupportsGateway: z.boolean(),
|
|
1543
|
+
token: HostingAiUsageTokenStatusSchema.nullable(),
|
|
1544
|
+
});
|
|
1545
|
+
/** Providers a customer may bring their own key for. */
|
|
1546
|
+
export const AiUsageByokProviderSchema = z.enum(["anthropic", "openai"]);
|
|
1547
|
+
/**
|
|
1548
|
+
* Body for `POST /projects/hosting/ai-usage`.
|
|
1549
|
+
*
|
|
1550
|
+
* Deliberately closed (`strictObject`): this route is gated on
|
|
1551
|
+
* `fusionHostingPublish` rather than `modifyProjectSettings`, so the request
|
|
1552
|
+
* shape is the only thing stopping it becoming a general prod-env write
|
|
1553
|
+
* primitive. The handler builds the env entries itself from `provider`,
|
|
1554
|
+
* `apiKey` and `baseUrl` — an env var *name* is never caller-supplied.
|
|
1555
|
+
*/
|
|
1556
|
+
export const SetHostingAiUsageRequestSchema = z.strictObject({
|
|
1557
|
+
projectId: z.string().min(1),
|
|
1558
|
+
mode: AiPaymentModeSchema,
|
|
1559
|
+
provider: AiUsageByokProviderSchema.optional(),
|
|
1560
|
+
apiKey: z.string().min(1).optional(),
|
|
1561
|
+
// https only: this value is written into a published site's prod env as
|
|
1562
|
+
// `OPENAI_BASE_URL`, so an `http://` endpoint would send the customer's
|
|
1563
|
+
// provider key and every prompt in plaintext from the deployed site.
|
|
1564
|
+
baseUrl: z
|
|
1565
|
+
.url()
|
|
1566
|
+
.refine((value) => value.startsWith("https://"), {
|
|
1567
|
+
message: "baseUrl must use https",
|
|
1568
|
+
})
|
|
1569
|
+
.optional(),
|
|
1570
|
+
});
|
|
1571
|
+
export const SetHostingAiUsageResponseSchema = z.object({
|
|
1572
|
+
aiPaymentMode: AiPaymentModeSchema,
|
|
1573
|
+
/**
|
|
1574
|
+
* `hosting.configUpdatedAt` after the write — compare against
|
|
1575
|
+
* `hosting.lastDeployAt` to decide whether a redeploy is needed. Absent when
|
|
1576
|
+
* the write changed nothing deploy-relevant (e.g. re-selecting the current
|
|
1577
|
+
* mode), which is exactly the case where no redeploy is needed.
|
|
1578
|
+
*/
|
|
1579
|
+
configUpdatedAt: z.number().optional(),
|
|
1580
|
+
});
|
|
1581
|
+
/** Body for `POST /projects/hosting/ai-usage/revoke`. */
|
|
1582
|
+
export const RevokeHostingAiTokenRequestSchema = z.strictObject({
|
|
1583
|
+
projectId: z.string().min(1),
|
|
1584
|
+
});
|
|
1585
|
+
export const RevokeHostingAiTokenResponseSchema = z.object({
|
|
1586
|
+
success: z.literal(true),
|
|
1587
|
+
});
|
|
1588
|
+
/**
|
|
1589
|
+
* The three prod env vars the AI-usage endpoint owns. The write path sets only
|
|
1590
|
+
* these; every non-`byok` mode deletes exactly these. Without the delete, a
|
|
1591
|
+
* BYOK→credits switch is a silent no-op — the stale provider key keeps winning
|
|
1592
|
+
* the framework's engine auto-detection, and the gateway injection gate keys on
|
|
1593
|
+
* its absence.
|
|
1594
|
+
*/
|
|
1595
|
+
export const AI_USAGE_MANAGED_PROD_ENV_KEYS = [
|
|
1596
|
+
"ANTHROPIC_API_KEY",
|
|
1597
|
+
"OPENAI_API_KEY",
|
|
1598
|
+
"OPENAI_BASE_URL",
|
|
1599
|
+
];
|
|
1464
1600
|
export const GetDeploysRequestSchema = z.object({
|
|
1465
1601
|
projectId: z.string(),
|
|
1466
1602
|
deployId: z.string().optional(),
|
|
@@ -1703,3 +1839,69 @@ export const EnsureMainBranchExistsOptionsSchema = z.object({
|
|
|
1703
1839
|
description: "Branch whose head ref seeds the main branch when it doesn't exist yet. No-op for non-internalHost projects.",
|
|
1704
1840
|
}),
|
|
1705
1841
|
});
|
|
1842
|
+
/**
|
|
1843
|
+
* `settings.environmentVariables` is stored with **plaintext** secret values;
|
|
1844
|
+
* secrets are masked before this shape leaves ai-services, same as
|
|
1845
|
+
* `Project.settings`.
|
|
1846
|
+
*/
|
|
1847
|
+
export const ProjectTemplateSchema = z.object({
|
|
1848
|
+
id: z.string(),
|
|
1849
|
+
name: z.string(),
|
|
1850
|
+
ownerId: z.string(),
|
|
1851
|
+
description: z.string(),
|
|
1852
|
+
repoFullName: z.string(),
|
|
1853
|
+
repoProvider: GitProviderSchema,
|
|
1854
|
+
repoUrl: z.string(),
|
|
1855
|
+
repoPrivate: z.boolean(),
|
|
1856
|
+
screenshot: z.string().nullish(),
|
|
1857
|
+
order: z.number(),
|
|
1858
|
+
hidden: z.boolean(),
|
|
1859
|
+
isDefault: z.boolean(),
|
|
1860
|
+
createdDate: z.string(),
|
|
1861
|
+
updatedAt: z.string(),
|
|
1862
|
+
createdBy: z.string(),
|
|
1863
|
+
lastUpdateBy: z.string(),
|
|
1864
|
+
sourceProjectId: z.string(),
|
|
1865
|
+
sourceBranchName: z.string(),
|
|
1866
|
+
settings: ProjectSettingsSchema.partial(),
|
|
1867
|
+
hosting: ProjectHostingSchema.optional(),
|
|
1868
|
+
});
|
|
1869
|
+
/**
|
|
1870
|
+
* The id, ownership, audit, and source fields are server-assigned, so they can't
|
|
1871
|
+
* be overridden here.
|
|
1872
|
+
*
|
|
1873
|
+
* A masked-secret placeholder in `settings.environmentVariables` keeps the
|
|
1874
|
+
* source project's real value, so a client that never saw a secret can still
|
|
1875
|
+
* round-trip it.
|
|
1876
|
+
*/
|
|
1877
|
+
export const ProjectTemplateOverridesSchema = z.object({
|
|
1878
|
+
...ProjectTemplateSchema.omit({
|
|
1879
|
+
id: true,
|
|
1880
|
+
ownerId: true,
|
|
1881
|
+
createdDate: true,
|
|
1882
|
+
updatedAt: true,
|
|
1883
|
+
createdBy: true,
|
|
1884
|
+
lastUpdateBy: true,
|
|
1885
|
+
sourceProjectId: true,
|
|
1886
|
+
sourceBranchName: true,
|
|
1887
|
+
}).partial().shape,
|
|
1888
|
+
hosting: ProjectHostingExternalSchema.optional(),
|
|
1889
|
+
});
|
|
1890
|
+
export const CreateProjectTemplateFromProjectOptionsSchema = z.object({
|
|
1891
|
+
sourceProjectId: z.string().min(1).meta({
|
|
1892
|
+
description: "Project whose settings, repo fields, and hosting config seed the template.",
|
|
1893
|
+
}),
|
|
1894
|
+
sourceBranchName: z.string().min(1).meta({
|
|
1895
|
+
description: "Branch of the source project that projects created from this template are seeded from.",
|
|
1896
|
+
}),
|
|
1897
|
+
overrides: ProjectTemplateOverridesSchema.optional().meta({
|
|
1898
|
+
description: "Fields to override on top of the values copied from the source project.",
|
|
1899
|
+
}),
|
|
1900
|
+
});
|
|
1901
|
+
/**
|
|
1902
|
+
* Deliberately minimal: the stored template carries plaintext secret env values,
|
|
1903
|
+
* so nothing beyond the identity of the new template goes back to the caller.
|
|
1904
|
+
*/
|
|
1905
|
+
export const CreateProjectTemplateFromProjectResponseSchema = z.object({
|
|
1906
|
+
template: ProjectTemplateSchema.pick({ id: true, name: true }),
|
|
1907
|
+
});
|
package/src/projects.test.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { AGENT_NATIVE_STARTER_REPO, matchesAgentNativeStarter, } from "./projects";
|
|
2
|
+
import { AGENT_NATIVE_STARTER_REPO, matchesAgentNativeStarter, ProjectHostingExternalSchema, ProjectHostingSchema, } from "./projects";
|
|
3
|
+
describe("project hosting schemas", () => {
|
|
4
|
+
it("accepts the canonical published branch route", () => {
|
|
5
|
+
expect(ProjectHostingSchema.parse({ publishedBranchId: "branch-live" })).toEqual({ publishedBranchId: "branch-live" });
|
|
6
|
+
});
|
|
7
|
+
it("does not allow clients to write the canonical published branch route", () => {
|
|
8
|
+
expect(ProjectHostingExternalSchema.parse({
|
|
9
|
+
enabled: true,
|
|
10
|
+
publishedBranchId: "branch-client",
|
|
11
|
+
})).toEqual({ enabled: true });
|
|
12
|
+
});
|
|
13
|
+
});
|
|
3
14
|
describe("matchesAgentNativeStarter", () => {
|
|
4
15
|
it("matches when repoName is the starter", () => {
|
|
5
16
|
expect(matchesAgentNativeStarter({ repoName: AGENT_NATIVE_STARTER_REPO })).toBe(true);
|