diff --git a/web/entrypoint.sh b/web/entrypoint.sh index 62345724d7..fd4c579a63 100755 --- a/web/entrypoint.sh +++ b/web/entrypoint.sh @@ -69,6 +69,10 @@ case "$(printf '%s' "${AGENTA_SANDBOX_LOCAL_ALLOWED:-true}" | tr '[:upper:]' '[: *) export AGENTA_SANDBOX_LOCAL_ENABLED="false" ;; esac +# Expose the full enabled-provider set (normalized to a lowercase, whitespace-free comma +# list) so the picker can restrict its options to exactly what this deployment enabled. +export AGENTA_ENABLED_SANDBOX_PROVIDERS="$(printf '%s' "${AGENTA_RUNNER_ENABLED_SANDBOX_PROVIDERS:-local}" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')" + mkdir -p "${ENTRYPOINT_DIR}/${AGENTA_LICENSE}/public" # Derive frontend auth feature flags @@ -209,6 +213,7 @@ window.__env = { NEXT_PUBLIC_SUPERTOKENS_PASSWORD_MAX_LENGTH: "${SUPERTOKENS_PASSWORD_MAX_LENGTH}", NEXT_PUBLIC_SUPERTOKENS_PASSWORD_REGEX: "${SUPERTOKENS_PASSWORD_REGEX}", NEXT_PUBLIC_AGENTA_SANDBOX_LOCAL_ENABLED: "${AGENTA_SANDBOX_LOCAL_ENABLED}", + NEXT_PUBLIC_AGENTA_ENABLED_SANDBOX_PROVIDERS: "${AGENTA_ENABLED_SANDBOX_PROVIDERS}", }; EOF diff --git a/web/oss/src/lib/helpers/dynamicEnv.ts b/web/oss/src/lib/helpers/dynamicEnv.ts index 09654d18a3..02f20cc286 100644 --- a/web/oss/src/lib/helpers/dynamicEnv.ts +++ b/web/oss/src/lib/helpers/dynamicEnv.ts @@ -63,6 +63,8 @@ export const processEnv = { NEXT_PUBLIC_AGENTA_TOOLS_ENABLED: process.env.NEXT_PUBLIC_AGENTA_TOOLS_ENABLED, NEXT_PUBLIC_AGENTA_BILLING_ENABLED: process.env.NEXT_PUBLIC_AGENTA_BILLING_ENABLED, NEXT_PUBLIC_AGENTA_SANDBOX_LOCAL_ENABLED: process.env.NEXT_PUBLIC_AGENTA_SANDBOX_LOCAL_ENABLED, + NEXT_PUBLIC_AGENTA_ENABLED_SANDBOX_PROVIDERS: + process.env.NEXT_PUBLIC_AGENTA_ENABLED_SANDBOX_PROVIDERS, NEXT_PUBLIC_SUPERTOKENS_PASSWORD_MIN_LENGTH: process.env.NEXT_PUBLIC_SUPERTOKENS_PASSWORD_MIN_LENGTH, NEXT_PUBLIC_SUPERTOKENS_PASSWORD_MAX_LENGTH: @@ -87,6 +89,16 @@ export const isSandboxLocalEnabled = () => { return SANDBOX_LOCAL_TRUTHY.has(raw.trim().toLowerCase()) } +// The sandbox providers this deployment enabled, normalized to lowercase ids. Unset/empty +// falls back to ["local"] so the picker never hides every option. +export const getEnabledSandboxProviders = (): string[] => { + const providers = getEnv("NEXT_PUBLIC_AGENTA_ENABLED_SANDBOX_PROVIDERS") + .split(",") + .map((provider) => provider.trim().toLowerCase()) + .filter(Boolean) + return providers.length > 0 ? providers : ["local"] +} + export const getEffectiveAuthConfig = () => { const googleOAuthClientId = getEnv("NEXT_PUBLIC_AGENTA_AUTH_GOOGLE_OAUTH_CLIENT_ID") const googleWorkspacesOAuthClientId = getEnv( diff --git a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/useModelHarness.tsx b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/useModelHarness.tsx index 130317101b..b81a6244fa 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/useModelHarness.tsx +++ b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/useModelHarness.tsx @@ -11,7 +11,7 @@ import { } from "@agenta/entities/secret" import type {SchemaProperty} from "@agenta/entities/shared" import {harnessCapabilitiesAtomFamily} from "@agenta/entities/workflow" -import {isSandboxLocalEnabled} from "@agenta/shared/api" +import {getEnabledSandboxProviders} from "@agenta/shared/api" import {normalizeProviderFamily} from "@agenta/shared/utils" import {ConfigAccordionSection} from "@agenta/ui/components/presentational" import {useDrillInUI} from "@agenta/ui/drill-in" @@ -98,8 +98,8 @@ export function useModelHarness({ const runnerProps = subProps("runner") const sandboxProps = subProps("sandbox") const sandboxOptions = useMemo(() => { - const options = getEnumOptions(sandboxProps.kind) - return isSandboxLocalEnabled() ? options : options.filter((o) => o.value !== "local") + const enabled = new Set(getEnabledSandboxProviders()) + return getEnumOptions(sandboxProps.kind).filter((o) => enabled.has(o.value)) }, [sandboxProps.kind]) const asObject = useCallback( @@ -715,7 +715,7 @@ export function useModelHarness({ /> ) : null} - {sandboxProps.permissions ? ( + {sandboxProps.permissions && sandbox.kind !== "local" ? ( <> {permissionOverrideHint} {/* Renders its knobs as peer RailField rows (Network egress / Filesystem diff --git a/web/packages/agenta-shared/src/api/env.ts b/web/packages/agenta-shared/src/api/env.ts index 45e8085681..2690758dec 100644 --- a/web/packages/agenta-shared/src/api/env.ts +++ b/web/packages/agenta-shared/src/api/env.ts @@ -37,6 +37,8 @@ export const processEnv = { NEXT_PUBLIC_LOG_APP_ATOMS: process.env.NEXT_PUBLIC_LOG_APP_ATOMS, NEXT_PUBLIC_ENABLE_ATOM_LOGS: process.env.NEXT_PUBLIC_ENABLE_ATOM_LOGS, NEXT_PUBLIC_AGENTA_SANDBOX_LOCAL_ENABLED: process.env.NEXT_PUBLIC_AGENTA_SANDBOX_LOCAL_ENABLED, + NEXT_PUBLIC_AGENTA_ENABLED_SANDBOX_PROVIDERS: + process.env.NEXT_PUBLIC_AGENTA_ENABLED_SANDBOX_PROVIDERS, } /** @@ -81,3 +83,13 @@ export const isSandboxLocalEnabled = (): boolean => { const raw = getEnv("NEXT_PUBLIC_AGENTA_SANDBOX_LOCAL_ENABLED") || "true" return SANDBOX_LOCAL_TRUTHY.has(raw.trim().toLowerCase()) } + +/** The sandbox providers this deployment enabled, normalized to lowercase ids. Unset/empty + * falls back to `["local"]` so the picker never hides every option. */ +export const getEnabledSandboxProviders = (): string[] => { + const providers = getEnv("NEXT_PUBLIC_AGENTA_ENABLED_SANDBOX_PROVIDERS") + .split(",") + .map((provider) => provider.trim().toLowerCase()) + .filter(Boolean) + return providers.length > 0 ? providers : ["local"] +} diff --git a/web/packages/agenta-shared/src/api/index.ts b/web/packages/agenta-shared/src/api/index.ts index da168783c1..ea8aff5e0a 100644 --- a/web/packages/agenta-shared/src/api/index.ts +++ b/web/packages/agenta-shared/src/api/index.ts @@ -2,7 +2,14 @@ * API utilities for Agenta packages. */ -export {getEnv, getAgentaApiUrl, getAgentaWebUrl, isSandboxLocalEnabled, processEnv} from "./env" +export { + getEnv, + getAgentaApiUrl, + getAgentaWebUrl, + isSandboxLocalEnabled, + getEnabledSandboxProviders, + processEnv, +} from "./env" export { axios, createAxiosInstance,