Skip to content

Commit f33a839

Browse files
authored
feat: use site caps for AIG gating, add support to dev-exec and serve (#7904)
* feat: use site caps for AIG gating, add support to dev-exec and serve * address nits
1 parent 8c9178d commit f33a839

6 files changed

Lines changed: 89 additions & 24 deletions

File tree

src/commands/dev-exec/dev-exec.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { OptionValues } from 'commander'
22
import execa from 'execa'
33

4-
import { getDotEnvVariables, injectEnvVariables } from '../../utils/dev.js'
4+
import { parseAIGatewayContext, setupAIGateway } from '@netlify/ai/bootstrap'
5+
6+
import { NETLIFYDEVLOG, log } from '../../utils/command-helpers.js'
7+
import { getDotEnvVariables, getSiteInformation, injectEnvVariables } from '../../utils/dev.js'
58
import { getEnvelopeEnv } from '../../utils/env/index.js'
69
import BaseCommand from '../base-command.js'
710

@@ -10,9 +13,33 @@ export const devExec = async (cmd: string, options: OptionValues, command: BaseC
1013

1114
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1215
const withEnvelopeEnvVars = await getEnvelopeEnv({ api, context: options.context, env: cachedConfig.env, siteInfo })
13-
const withDotEnvVars = await getDotEnvVariables({ devConfig: { ...config.dev }, env: withEnvelopeEnvVars, site })
16+
const env = await getDotEnvVariables({ devConfig: { ...config.dev }, env: withEnvelopeEnvVars, site })
17+
18+
const { capabilities, siteUrl } = await getSiteInformation({
19+
offline: false,
20+
api,
21+
site,
22+
siteInfo,
23+
})
24+
25+
if (!capabilities.aiGatewayDisabled) {
26+
await setupAIGateway({ api, env, siteID: site.id, siteURL: siteUrl })
27+
28+
const aiGatewayEnv = env.AI_GATEWAY as (typeof env)[string] | undefined
29+
if (aiGatewayEnv) {
30+
const aiGatewayContext = parseAIGatewayContext(aiGatewayEnv.value)
31+
if (aiGatewayContext?.envVars) {
32+
for (const envVar of aiGatewayContext.envVars) {
33+
env[envVar.key] = { sources: ['internal'], value: aiGatewayContext.token }
34+
env[envVar.url] = { sources: ['internal'], value: aiGatewayContext.url }
35+
}
36+
}
37+
}
38+
} else {
39+
log(`${NETLIFYDEVLOG} AI Gateway is disabled for this account`)
40+
}
1441

15-
injectEnvVariables(withDotEnvVars)
42+
injectEnvVariables(env)
1643

1744
await execa(cmd, command.args.slice(1), {
1845
stdio: 'inherit',

src/commands/dev/dev.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,18 +158,17 @@ export const dev = async (options: OptionValues, command: BaseCommand) => {
158158
if (!options.offline && !options.offlineEnv && !capabilities.aiGatewayDisabled) {
159159
await setupAIGateway({ api, env, siteID: site.id, siteURL: siteUrl })
160160

161-
// Parse AI Gateway context and inject provider API keys
162-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- AI_GATEWAY is conditionally set by setupAIGateway
163-
if (env.AI_GATEWAY) {
164-
const aiGatewayContext = parseAIGatewayContext(env.AI_GATEWAY.value)
161+
const aiGatewayEnv = env.AI_GATEWAY as (typeof env)[string] | undefined
162+
if (aiGatewayEnv) {
163+
const aiGatewayContext = parseAIGatewayContext(aiGatewayEnv.value)
165164
if (aiGatewayContext?.envVars) {
166165
for (const envVar of aiGatewayContext.envVars) {
167166
env[envVar.key] = { sources: ['internal'], value: aiGatewayContext.token }
168167
env[envVar.url] = { sources: ['internal'], value: aiGatewayContext.url }
169168
}
170169
}
171170
}
172-
} else if (capabilities.aiGatewayDisabled) {
171+
} else if (!options.offline && !options.offlineEnv && capabilities.aiGatewayDisabled) {
173172
log(`${NETLIFYDEVLOG} AI Gateway is disabled for this account`)
174173
}
175174

src/commands/functions/functions-serve.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export const functionsServe = async (options: OptionValues, command: BaseCommand
4141

4242
if (!options.offline && !capabilities.aiGatewayDisabled) {
4343
await setupAIGateway({ api, env, siteID: site.id, siteURL: siteUrl })
44-
} else if (capabilities.aiGatewayDisabled) {
44+
} else if (!options.offline && capabilities.aiGatewayDisabled) {
4545
log(`${NETLIFYDEVLOG} AI Gateway is disabled for this account`)
4646
}
4747

src/commands/serve/serve.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import process from 'process'
22

3+
import { parseAIGatewayContext, setupAIGateway } from '@netlify/ai/bootstrap'
34
import type { OptionValues } from 'commander'
45

56
import {
@@ -56,8 +57,6 @@ export const serve = async (options: OptionValues, command: BaseCommand) => {
5657
}
5758

5859
env = await getDotEnvVariables({ devConfig, env, site })
59-
injectEnvVariables(env)
60-
await promptEditorHelper({ chalk, config, log, NETLIFYDEVLOG, repositoryRoot, state })
6160

6261
const { accountId, addonsUrls, capabilities, siteUrl, timeouts } = await getSiteInformation({
6362
// inherited from base command --offline
@@ -67,6 +66,26 @@ export const serve = async (options: OptionValues, command: BaseCommand) => {
6766
siteInfo,
6867
})
6968

69+
if (!options.offline && !capabilities.aiGatewayDisabled) {
70+
await setupAIGateway({ api, env, siteID: site.id, siteURL: siteUrl })
71+
72+
const aiGatewayEnv = env.AI_GATEWAY as (typeof env)[string] | undefined
73+
if (aiGatewayEnv) {
74+
const aiGatewayContext = parseAIGatewayContext(aiGatewayEnv.value)
75+
if (aiGatewayContext?.envVars) {
76+
for (const envVar of aiGatewayContext.envVars) {
77+
env[envVar.key] = { sources: ['internal'], value: aiGatewayContext.token }
78+
env[envVar.url] = { sources: ['internal'], value: aiGatewayContext.url }
79+
}
80+
}
81+
}
82+
} else if (!options.offline && capabilities.aiGatewayDisabled) {
83+
log(`${NETLIFYDEVLOG} AI Gateway is disabled for this account`)
84+
}
85+
86+
injectEnvVariables(env)
87+
await promptEditorHelper({ chalk, config, log, NETLIFYDEVLOG, repositoryRoot, state })
88+
7089
if (!site.root) {
7190
throw new Error('Site root not found')
7291
}

src/utils/dev.ts

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -118,18 +118,33 @@ const SYNCHRONOUS_FUNCTION_TIMEOUT = 30
118118
// default 15 minutes for background functions
119119
const BACKGROUND_FUNCTION_TIMEOUT = 900
120120

121-
/**
122-
*
123-
* @param {object} config
124-
* @param {boolean} config.offline
125-
* @param {*} config.api
126-
* @param {*} config.site
127-
* @param {*} config.siteInfo
128-
* @returns
129-
*/
121+
interface GetSiteInformationOptions {
122+
api: NetlifyAPI
123+
offline: boolean
124+
site: { id?: string }
125+
siteInfo: SiteInfo
126+
}
130127

131-
// @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message
132-
export const getSiteInformation = async ({ api, offline, site, siteInfo }) => {
128+
export interface SiteInformationResult {
129+
addonsUrls: Record<string, string>
130+
siteUrl: string
131+
accountId?: string
132+
capabilities: {
133+
backgroundFunctions?: boolean
134+
aiGatewayDisabled: boolean
135+
}
136+
timeouts: {
137+
syncFunctions: number
138+
backgroundFunctions: number
139+
}
140+
}
141+
142+
export const getSiteInformation = async ({
143+
api,
144+
offline,
145+
site,
146+
siteInfo,
147+
}: GetSiteInformationOptions): Promise<SiteInformationResult> => {
133148
if (site.id && !offline) {
134149
validateSiteInfo({ site, siteInfo })
135150
const [accounts, addons] = await Promise.all([getAccounts({ api }), getAddons({ api, site })])
@@ -143,7 +158,7 @@ export const getSiteInformation = async ({ api, offline, site, siteInfo }) => {
143158
accountId: account?.id,
144159
capabilities: {
145160
backgroundFunctions: supportsBackgroundFunctions(account),
146-
aiGatewayDisabled: account?.capabilities?.ai_gateway_disabled?.included ?? false,
161+
aiGatewayDisabled: siteInfo.capabilities?.ai_gateway_disabled ?? false,
147162
},
148163
timeouts: {
149164
syncFunctions: siteInfo.functions_timeout ?? siteInfo.functions_config?.timeout ?? SYNCHRONOUS_FUNCTION_TIMEOUT,

src/utils/types.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,16 @@ export interface SiteInfo {
9595
repo_url: string
9696
}
9797
| undefined
98-
capabilities: Record<string, unknown>
98+
capabilities?: {
99+
ai_gateway_disabled?: boolean
100+
[key: string]: unknown
101+
}
99102
created_at: string
100103
custom_domain: string
101104
deploy_hook: string
102105
deploy_url: string
106+
functions_timeout?: number
107+
functions_config?: { timeout?: number }
103108
dev_server_settings?:
104109
| {
105110
cmd: string

0 commit comments

Comments
 (0)