refactor(exa-app): drop EXA_API_KEY env var after flag-based install

Removes the last coupling of Exa to deployment-time config after the
infrastructure PR moved pre-installation to a registration flag:

- Drops the `EXA_API_KEY` env var. Exa is now registered as an
  `ApplicationRegistration` and its API key lives on the registration's
  `ApplicationRegistrationVariable` row, same as any other app.
- Updates the Exa app manifest + README to describe the admin flow
  (register app, flip `autoInstallOnNewWorkspaces`, set EXA_API_KEY
  server variable) instead of env-var seeding.
- Updates the AI chat preload TODO comment to reflect the flag-based
  install model.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Félix Malfait
2026-04-22 22:40:24 +02:00
co-authored by Claude Opus 4.7
parent c4c2898db9
commit 651166be36
4 changed files with 11 additions and 26 deletions
+9 -9
View File
@@ -3,16 +3,16 @@
Exposes [Exa](https://exa.ai) structured web search to Twenty AI agents
(chat + workflow agents + MCP) as the `app_exa_web_search` tool.
## Configuration
## Installation
Server admin sets two env vars:
- `PRE_INSTALLED_APPS=@twenty-apps/exa` — installs this app on every new
workspace. Existing workspaces backfill via the
`install-pre-installed-apps` CLI command.
- `EXA_API_KEY=<key>` — auto-seeded into the application registration's
server variables at server bootstrap. No per-workspace configuration
needed.
1. Register the app on the Twenty server once (admin API / UI):
`@twenty-apps/exa` from npm.
2. Set `autoInstallOnNewWorkspaces=true` on the registration so it's
installed on every new workspace. Existing workspaces can be
backfilled via the `install-pre-installed-apps` CLI command.
3. Set the `EXA_API_KEY` server variable on the registration to your Exa
API key. The value is injected into every logic function execution
no per-workspace configuration needed.
## Billing
@@ -12,7 +12,7 @@ export default defineApplication({
serverVariables: {
EXA_API_KEY: {
description:
'Exa API key. Server admins can set this via the EXA_API_KEY env var, which is auto-seeded into this application registration at server bootstrap.',
'Exa API key. Set by the server admin on this registration after installation; the value is injected into every logic function execution.',
isSecret: true,
isRequired: true,
},
@@ -4,7 +4,6 @@ import { plainToClass } from 'class-transformer';
import {
IsDefined,
IsOptional,
IsString,
IsUrl,
ValidateIf,
type ValidationError,
@@ -13,7 +12,6 @@ import {
import { isDefined } from 'twenty-shared/utils';
import { type LoggerOptions } from 'typeorm/logger/LoggerOptions';
import { parsePreInstalledApps } from 'src/engine/core-modules/application/pre-installed-apps/utils/parse-pre-installed-apps.util';
import { LogicFunctionDriverType } from 'src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface';
import { type AwsRegion } from 'src/engine/core-modules/twenty-config/interfaces/aws-region.interface';
import { NodeEnvironment } from 'src/engine/core-modules/twenty-config/interfaces/node-environment.interface';
@@ -676,19 +674,6 @@ export class ConfigVariables {
@CastToPositiveNumber()
CODE_INTERPRETER_TIMEOUT_MS = 300_000;
@ConfigVariablesMetadata({
group: ConfigVariablesGroup.LLM,
description:
'Exa API key. Seeded into the @twenty-apps/exa application registration as a server-level variable at bootstrap, so logic function handlers can read it from their execution env. Required only when the Exa app is listed in PRE_INSTALLED_APPS.',
type: ConfigVariableType.STRING,
isSensitive: true,
})
@ValidateIf((env) =>
parsePreInstalledApps(env.PRE_INSTALLED_APPS).includes('@twenty-apps/exa'),
)
@IsString()
EXA_API_KEY?: string;
@ConfigVariablesMetadata({
group: ConfigVariablesGroup.ANALYTICS_CONFIG,
description: 'Enable or disable analytics for telemetry',
@@ -142,7 +142,7 @@ export class ChatExecutionService {
// Preload the Exa app tool (shipped as `@twenty-apps/exa`) so chat has
// structured web search ready without discovery. getToolsByName silently
// skips the entry when the workspace doesn't have the Exa app installed
// (admin didn't add it to PRE_INSTALLED_APPS or backfill hasn't run).
// (admin hasn't registered it + flipped `autoInstallOnNewWorkspaces`).
// TODO(app-preloading): move this list into the app manifest so any
// app can declare `preloadedInChat: true` instead of hardcoding here.
const toolNamesToPreload = [...COMMON_PRELOAD_TOOLS, 'app_exa_web_search'];