@opengeni/config 2.1.1 → 3.0.0-canary.36239117573001

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/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Entitlements, LatencyMode, ReasoningEffort, SandboxBackend, StaticUsageLimits, TurnExecutionPolicyV1, type TurnExecutionLatencyModeSourceV1, type TurnExecutionModelSourceV1, type TurnExecutionReasoningSourceV1, type VideoGenerationResolution, type FirstPartyMcpToolName as FirstPartyMcpToolNameType } from "@opengeni/contracts";
2
+ import type { CodeSearchDeploymentPolicy } from "@opengeni/contracts/code-search";
2
3
  export { XAI_SUBSCRIPTION_MODEL_ID_PREFIX } from "@opengeni/xai-subscription";
3
4
  import { z } from "zod";
4
5
  export declare const DEFAULT_OPENROUTER_MODEL_ID: "openrouter/nvidia/nemotron-3-super-120b-a12b:free";
@@ -128,8 +129,10 @@ declare const SettingsSchema: z.ZodObject<{
128
129
  analyticsPosthogHost: z.ZodOptional<z.ZodString>;
129
130
  analyticsGa4MeasurementId: z.ZodOptional<z.ZodString>;
130
131
  publicBaseUrl: z.ZodOptional<z.ZodString>;
132
+ documentationUrl: z.ZodPreprocess<z.ZodDefault<z.ZodNullable<z.ZodURL>>>;
131
133
  mcpOauthEnabled: z.ZodDefault<z.ZodPreprocess<z.ZodBoolean>>;
132
- mcpOauthTrustedProxyHops: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
134
+ apiTrustedProxyHops: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
135
+ apiTrustedProxyCidrs: z.ZodDefault<z.ZodString>;
133
136
  webBaseUrl: z.ZodOptional<z.ZodString>;
134
137
  agentReleasesBaseUrl: z.ZodDefault<z.ZodString>;
135
138
  agentStableVersion: z.ZodDefault<z.ZodString>;
@@ -196,6 +199,7 @@ declare const SettingsSchema: z.ZodObject<{
196
199
  computer_targets: "computer_targets";
197
200
  connected_machine_enroll_token: "connected_machine_enroll_token";
198
201
  connected_machine_remove: "connected_machine_remove";
202
+ custom_mcp_setup_request: "custom_mcp_setup_request";
199
203
  editable_artifact_apply: "editable_artifact_apply";
200
204
  editable_artifact_create: "editable_artifact_create";
201
205
  editable_artifact_export: "editable_artifact_export";
@@ -368,6 +372,7 @@ declare const SettingsSchema: z.ZodObject<{
368
372
  computer_targets: "computer_targets";
369
373
  connected_machine_enroll_token: "connected_machine_enroll_token";
370
374
  connected_machine_remove: "connected_machine_remove";
375
+ custom_mcp_setup_request: "custom_mcp_setup_request";
371
376
  editable_artifact_apply: "editable_artifact_apply";
372
377
  editable_artifact_create: "editable_artifact_create";
373
378
  editable_artifact_export: "editable_artifact_export";
@@ -561,10 +566,12 @@ declare const SettingsSchema: z.ZodObject<{
561
566
  authAllowMetrics: z.ZodDefault<z.ZodPreprocess<z.ZodBoolean>>;
562
567
  apiHost: z.ZodDefault<z.ZodString>;
563
568
  apiPort: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
569
+ apiMetricsPort: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
564
570
  workerHttpPort: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
565
571
  opengeniMcpInternalUrl: z.ZodOptional<z.ZodString>;
566
572
  opengeniMcpUrl: z.ZodOptional<z.ZodString>;
567
573
  corsAllowOriginRegex: z.ZodDefault<z.ZodString>;
574
+ localAllowedOrigins: z.ZodOptional<z.ZodString>;
568
575
  openaiProvider: z.ZodDefault<z.ZodEnum<{
569
576
  azure: "azure";
570
577
  openai: "openai";
@@ -636,6 +643,16 @@ declare const SettingsSchema: z.ZodObject<{
636
643
  xhigh: "xhigh";
637
644
  }>>;
638
645
  openaiAllowedReasoningEfforts: z.ZodDefault<z.ZodString>;
646
+ creditsDefaultModel: z.ZodDefault<z.ZodString>;
647
+ creditsDefaultReasoningEffort: z.ZodDefault<z.ZodEnum<{
648
+ high: "high";
649
+ low: "low";
650
+ max: "max";
651
+ medium: "medium";
652
+ minimal: "minimal";
653
+ none: "none";
654
+ xhigh: "xhigh";
655
+ }>>;
639
656
  openaiResponsesTransport: z.ZodDefault<z.ZodEnum<{
640
657
  http: "http";
641
658
  websocket: "websocket";
@@ -647,6 +664,16 @@ declare const SettingsSchema: z.ZodObject<{
647
664
  openaiReasoningEncryptedContent: z.ZodDefault<z.ZodPreprocess<z.ZodBoolean>>;
648
665
  openaiMaxRetries: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
649
666
  webSearchEnabled: z.ZodDefault<z.ZodPreprocess<z.ZodBoolean>>;
667
+ jevApiKey: z.ZodOptional<z.ZodString>;
668
+ jevBaseUrl: z.ZodDefault<z.ZodString>;
669
+ jevModel: z.ZodDefault<z.ZodString>;
670
+ jevRequestTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
671
+ codeSearchMode: z.ZodDefault<z.ZodEnum<{
672
+ default_on: "default_on";
673
+ experiment: "experiment";
674
+ off: "off";
675
+ opt_in: "opt_in";
676
+ }>>;
650
677
  agentInstructionsTemplate: z.ZodDefault<z.ZodString>;
651
678
  azureOpenaiBaseUrl: z.ZodOptional<z.ZodString>;
652
679
  azureOpenaiEndpoint: z.ZodOptional<z.ZodString>;
@@ -1077,6 +1104,15 @@ export type VoiceInputProviderConfig = {
1077
1104
  kind: "supergrok-subscription";
1078
1105
  experimental: true;
1079
1106
  };
1107
+ /** The deployment's Jev key, or undefined when it is missing or a placeholder. */
1108
+ export declare function usableJevApiKey(settings: Pick<Settings, "jevApiKey">): string | undefined;
1109
+ /**
1110
+ * Deployment half of the `code_search` decision. `available` is false when
1111
+ * the mode is off or no usable Jev key is configured; workspaces then cannot
1112
+ * turn it on. `workspaceDefault` applies to workspaces without their own
1113
+ * setting.
1114
+ */
1115
+ export declare function codeSearchDeploymentPolicy(settings: Pick<Settings, "codeSearchMode" | "jevApiKey">): CodeSearchDeploymentPolicy;
1080
1116
  /**
1081
1117
  * Reject empty / template secrets so `.env.example` placeholders like
1082
1118
  * `your-key` cannot advertise voice input as available and then 401 upstream.
@@ -2404,6 +2440,20 @@ export interface ConfiguredModel {
2404
2440
  reasoningEffort: boolean;
2405
2441
  hostedWebSearch: boolean;
2406
2442
  }
2443
+ /**
2444
+ * Whether a resolved route is the deployment-funded OpenRouter free tier: the
2445
+ * managed OpenRouter provider, which sends every workspace's requests with the
2446
+ * deployment's one OpenRouter key, serving an upstream `:free` variant (the
2447
+ * curated catalog admits nothing else). OpenRouter limits free-variant
2448
+ * requests per account per minute and per day, so each request on this route
2449
+ * spends quota that every turn on the deployment shares. A workspace or
2450
+ * organization OpenRouter connection uses its own account and is not this
2451
+ * route.
2452
+ */
2453
+ export declare function isManagedOpenRouterFreeRoute(route: {
2454
+ provider: Pick<ResolvedModelProvider, "kind">;
2455
+ configured: Pick<ConfiguredModel, "upstreamModelId">;
2456
+ }): boolean;
2407
2457
  export declare const VERCEL_AI_GATEWAY_BASE_URL: "https://ai-gateway.vercel.sh/v1";
2408
2458
  export declare const VERCEL_AI_GATEWAY_AI_SDK_BASE_URL: "https://ai-gateway.vercel.sh/v4/ai";
2409
2459
  export declare const VERCEL_AI_GATEWAY_CONNECTION_DOMAIN: "ai-gateway.vercel.sh";
@@ -2960,6 +3010,31 @@ export declare function firstPartyMcpInternalBaseUrl(settings: Settings): string
2960
3010
  export declare function firstPartyMcpWorkspaceUrl(settings: Settings, workspaceId: string): string;
2961
3011
  export declare function firstPartyMcpInternalWorkspaceUrl(settings: Settings, workspaceId: string): string;
2962
3012
  export declare function codemodeWorkspaceUrl(settings: Settings, workspaceId: string): string;
3013
+ export type TrustedProxyCidr = {
3014
+ address: string;
3015
+ prefix: number;
3016
+ family: "ipv4" | "ipv6";
3017
+ };
3018
+ /**
3019
+ * Parse `OPENGENI_API_TRUSTED_PROXY_CIDRS`: comma-separated IPv4/IPv6 CIDRs or
3020
+ * single addresses. Throws on any malformed entry so a typo can never widen or
3021
+ * silently disable the trusted proxy set.
3022
+ */
3023
+ export declare function trustedProxyCidrEntries(raw: string): TrustedProxyCidr[];
3024
+ /**
3025
+ * Parse `OPENGENI_LOCAL_ALLOWED_ORIGINS`: comma-separated exact browser origins
3026
+ * (`http(s)://host[:port]`, no path, query, credentials, or wildcard). Throws on
3027
+ * any malformed entry so a typo can never widen or silently disable the local
3028
+ * browser boundary.
3029
+ */
3030
+ export declare function localAllowedOriginEntries(raw: string | undefined): string[];
3031
+ /**
3032
+ * Local development only: whether a normalized API path is one a sandbox calls
3033
+ * (Codemode, first-party MCP, and the personal Git broker). The Linux Docker
3034
+ * sandbox route relays only these, and the local API serves only these on the
3035
+ * addresses that only sandboxes use (see `apps/api/src/http/local-browser-boundary.ts`).
3036
+ */
3037
+ export declare function localSandboxApiRouteAllowed(pathname: string): boolean;
2963
3038
  /** Validate one fully resolved, secret-bearing executable catalog. */
2964
3039
  export declare function validateModelCatalogSettings(settings: Settings, source?: NodeJS.ProcessEnv): ConfiguredModel[];
2965
3040
  /**
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  BillingMode,
4
4
  CAPABILITY_DESCRIPTORS,
5
5
  DEFAULT_FIRST_PARTY_MCP_TOOLS,
6
+ DEFAULT_OPENGENI_DOCUMENTATION_URL,
6
7
  currentAgentLearningToolSelection,
7
8
  Entitlements,
8
9
  EntitlementsMode,
@@ -44,6 +45,7 @@ import {
44
45
  } from "@opengeni/xai-subscription";
45
46
  import { XAI_SUBSCRIPTION_MODEL_ID_PREFIX as XAI_SUBSCRIPTION_MODEL_ID_PREFIX2 } from "@opengeni/xai-subscription";
46
47
  import { createHash } from "crypto";
48
+ import { isIP } from "net";
47
49
  import { z } from "zod";
48
50
  var envName = /^[A-Za-z_][A-Za-z0-9_]*$/;
49
51
  var registryId = /^[A-Za-z0-9_-]+$/;
@@ -231,12 +233,26 @@ var SettingsSchema = z.object({
231
233
  analyticsPosthogHost: z.string().url().max(2048).optional(),
232
234
  analyticsGa4MeasurementId: z.string().max(32).regex(/^G-[A-Z0-9]+$/u).optional(),
233
235
  publicBaseUrl: z.string().url().optional(),
236
+ // Product documentation the web console links from its Help menu. Absent
237
+ // means the public OpenGeni docs; `none` hides the link for deployments that
238
+ // publish no documentation of their own.
239
+ documentationUrl: z.preprocess(
240
+ (value) => typeof value === "string" ? value.trim().toLowerCase() === "none" ? null : value.trim() : value,
241
+ z.url({ protocol: /^https?$/u, error: "must be an absolute http(s) URL or none" }).nullable().default(DEFAULT_OPENGENI_DOCUMENTATION_URL)
242
+ ),
234
243
  // Standards-based OAuth authorization server for external workspace MCP
235
244
  // clients. Opt-in because it creates a new public authentication surface.
236
245
  mcpOauthEnabled: EnvBoolean.default(false),
237
- // Forwarded client addresses are ignored by default. Operators may trust an
246
+ // Client source address for every API rate limit, abuse quota, and auth
247
+ // session record (Better Auth, MCP OAuth registration, enrollment, account
248
+ // setup, login transactions). Forwarded client addresses are ignored by
249
+ // default so a caller cannot choose its own bucket. Operators may trust an
238
250
  // exact number of proxy hops only when direct access to the API is blocked.
239
- mcpOauthTrustedProxyHops: z.coerce.number().int().min(0).max(16).default(0),
251
+ apiTrustedProxyHops: z.coerce.number().int().min(0).max(16).default(0),
252
+ // Optional comma-separated CIDRs (or single addresses) of the proxy that
253
+ // connects to the API. When set, forwarded client addresses are honored
254
+ // only for requests whose transport peer is inside one of them.
255
+ apiTrustedProxyCidrs: z.string().default(""),
240
256
  // Browser origin when the web app and API use separate origins in local
241
257
  // development. Production normally leaves this unset and uses publicBaseUrl.
242
258
  webBaseUrl: z.string().url().optional(),
@@ -428,6 +444,9 @@ var SettingsSchema = z.object({
428
444
  authAllowMetrics: EnvBoolean.default(false),
429
445
  apiHost: z.string().default("0.0.0.0"),
430
446
  apiPort: z.coerce.number().int().positive().default(8e3),
447
+ // When set, GET /metrics is served only on this dedicated internal listener
448
+ // and never on the public API port that an ingress forwards to.
449
+ apiMetricsPort: z.coerce.number().int().positive().optional(),
431
450
  workerHttpPort: z.coerce.number().int().positive().default(8001),
432
451
  // Worker-side first-party MCP traffic stays on the deployment's internal
433
452
  // network. OPENGENI_MCP_URL remains the sandbox/external route used by
@@ -438,6 +457,11 @@ var SettingsSchema = z.object({
438
457
  // call the public API with bearer credentials, but never receive credentialed
439
458
  // CORS responses.
440
459
  corsAllowOriginRegex: z.string().default(String.raw`^https?://(localhost|127\.0\.0\.1)(:\d+)?$`),
460
+ // Local development only (`local` access mode in the `local` environment):
461
+ // extra exact browser origins, comma-separated, that may call the
462
+ // unauthenticated API besides this stack's own web origin. The local browser
463
+ // boundary ignores `corsAllowOriginRegex`.
464
+ localAllowedOrigins: z.string().optional(),
441
465
  openaiProvider: z.enum(["openai", "azure"]).default("openai"),
442
466
  openaiApiKey: z.string().optional(),
443
467
  openaiBaseUrl: z.string().optional(),
@@ -572,6 +596,18 @@ var SettingsSchema = z.object({
572
596
  reasoningConfigurationUpdatesEnabled: EnvBoolean.default(false),
573
597
  openaiReasoningEffort: ReasoningEffort.default("low"),
574
598
  openaiAllowedReasoningEfforts: z.string().default("low,medium,high,xhigh,max"),
599
+ // Default for new chats and scheduled tasks when the workspace has an
600
+ // OpenGeni credit balance, no saved workspace default, and no usable
601
+ // connected subscription, and the deployment default is not already a
602
+ // credits-billed model. Selected only when this model is selectable in the
603
+ // workspace catalog; otherwise the first selectable credits model is used.
604
+ // OPENGENI_CREDITS_DEFAULT_MODEL / OPENGENI_CREDITS_DEFAULT_REASONING_EFFORT.
605
+ creditsDefaultModel: z.string().trim().min(1).max(256).refine((value) => !/[\u000A\u000D|]/u.test(value), {
606
+ message: "credits default model must not contain newlines or the | field separator"
607
+ }).default("gpt-6-luna"),
608
+ // Preferred effort for the credits default. Clamped to the highest effort
609
+ // the selected model supports at or below this value.
610
+ creditsDefaultReasoningEffort: ReasoningEffort.default("xhigh"),
575
611
  openaiResponsesTransport: z.enum(["http", "websocket"]).default("http"),
576
612
  // Provider-assigned item ids (rs_/msg_/fc_…) in Responses API input are
577
613
  // resolved against the provider's server-side response store. That store is
@@ -597,6 +633,19 @@ var SettingsSchema = z.object({
597
633
  // merged with the MCP-server tools (getAllTools = [...mcpTools, ...tools])
598
634
  // and the sandbox capability tools, never replacing them.
599
635
  webSearchEnabled: EnvBoolean.default(true),
636
+ // Jev (TypeSafe's fast judge model) for worker-side agent tools. Without a
637
+ // usable key every Jev-backed feature is off. The key stays on the server
638
+ // (API and worker) and never reaches a sandbox or Connected Machine.
639
+ jevApiKey: z.string().optional(),
640
+ jevBaseUrl: z.string().url().default("https://api.typesafe.ai"),
641
+ jevModel: z.string().trim().min(1).max(128).default("jev-latest"),
642
+ jevRequestTimeoutMs: z.coerce.number().int().positive().max(12e4).default(1e4),
643
+ // Jev-backed `code_search` agent tool. `off` never offers it, `opt_in` offers
644
+ // it only where workspace settings enable it, `default_on` offers it
645
+ // everywhere except workspaces that disable it, and `experiment` gives it to
646
+ // a fixed half of sessions in workspaces without their own setting, for
647
+ // comparison. It also needs jevApiKey.
648
+ codeSearchMode: z.enum(["off", "opt_in", "default_on", "experiment"]).default("off"),
600
649
  // Deployment-default agent persona template (the white-label surface). The
601
650
  // runtime resolves the effective template per turn as
602
651
  // per-session-override > per-workspace override > this default, substitutes
@@ -1272,6 +1321,15 @@ function personalGitHubOAuthCallbackUrl(publicBaseUrl) {
1272
1321
  function usableDeploymentSecret(value) {
1273
1322
  return isUsableVoiceInputSecret(value) ? value : void 0;
1274
1323
  }
1324
+ function usableJevApiKey(settings) {
1325
+ return usableDeploymentSecret(settings.jevApiKey);
1326
+ }
1327
+ function codeSearchDeploymentPolicy(settings) {
1328
+ const available = settings.codeSearchMode !== "off" && usableJevApiKey(settings) !== void 0;
1329
+ if (!available) return { available: false, workspaceDefault: "off" };
1330
+ const workspaceDefault = settings.codeSearchMode === "default_on" ? "on" : settings.codeSearchMode === "experiment" ? "split" : "off";
1331
+ return { available, workspaceDefault };
1332
+ }
1275
1333
  function isUsableVoiceInputSecret(value) {
1276
1334
  if (value == null) return false;
1277
1335
  const trimmed = value.trim();
@@ -1939,6 +1997,9 @@ var IntegrationOAuthClientConfigSchema = z.object({
1939
1997
  clientSecret: z.string().min(1).optional(),
1940
1998
  tokenEndpointAuthMethod: z.enum(["none", "client_secret_post", "client_secret_basic"]).default("none")
1941
1999
  });
2000
+ function isManagedOpenRouterFreeRoute(route) {
2001
+ return route.provider.kind === "openrouter-managed" && route.configured.upstreamModelId.endsWith(":free");
2002
+ }
1942
2003
  var VERCEL_AI_GATEWAY_BASE_URL = "https://ai-gateway.vercel.sh/v1";
1943
2004
  var VERCEL_AI_GATEWAY_AI_SDK_BASE_URL = "https://ai-gateway.vercel.sh/v4/ai";
1944
2005
  var VERCEL_AI_GATEWAY_CONNECTION_DOMAIN = "ai-gateway.vercel.sh";
@@ -2377,8 +2438,10 @@ function getSettings(source = process.env) {
2377
2438
  analyticsPosthogHost: optional("OPENGENI_ANALYTICS_POSTHOG_HOST"),
2378
2439
  analyticsGa4MeasurementId: optional("OPENGENI_ANALYTICS_GA4_MEASUREMENT_ID"),
2379
2440
  publicBaseUrl: optional("OPENGENI_PUBLIC_BASE_URL"),
2441
+ documentationUrl: optional("OPENGENI_DOCUMENTATION_URL"),
2380
2442
  mcpOauthEnabled: optional("OPENGENI_MCP_OAUTH_ENABLED"),
2381
- mcpOauthTrustedProxyHops: optional("OPENGENI_MCP_OAUTH_TRUSTED_PROXY_HOPS"),
2443
+ apiTrustedProxyHops: optional("OPENGENI_API_TRUSTED_PROXY_HOPS"),
2444
+ apiTrustedProxyCidrs: optional("OPENGENI_API_TRUSTED_PROXY_CIDRS"),
2382
2445
  webBaseUrl: optional("OPENGENI_WEB_BASE_URL"),
2383
2446
  agentReleasesBaseUrl: optional("OPENGENI_AGENT_RELEASES_BASE_URL"),
2384
2447
  agentStableVersion: optional("OPENGENI_AGENT_STABLE_VERSION"),
@@ -2466,10 +2529,12 @@ function getSettings(source = process.env) {
2466
2529
  authAllowMetrics: optional("OPENGENI_AUTH_ALLOW_METRICS"),
2467
2530
  apiHost: optional("OPENGENI_API_HOST"),
2468
2531
  apiPort: optional("OPENGENI_API_PORT"),
2532
+ apiMetricsPort: optional("OPENGENI_API_METRICS_PORT"),
2469
2533
  workerHttpPort: optional("OPENGENI_WORKER_HTTP_PORT"),
2470
2534
  opengeniMcpInternalUrl: optional("OPENGENI_MCP_INTERNAL_URL"),
2471
2535
  opengeniMcpUrl: optional("OPENGENI_MCP_URL"),
2472
2536
  corsAllowOriginRegex: optional("OPENGENI_CORS_ALLOW_ORIGIN_REGEX"),
2537
+ localAllowedOrigins: optional("OPENGENI_LOCAL_ALLOWED_ORIGINS"),
2473
2538
  openaiProvider: optional("OPENGENI_OPENAI_PROVIDER"),
2474
2539
  openaiApiKey: optional("OPENGENI_OPENAI_API_KEY") ?? optional("OPENAI_API_KEY"),
2475
2540
  openaiBaseUrl: optional("OPENGENI_OPENAI_BASE_URL") ?? optional("OPENAI_BASE_URL"),
@@ -2541,11 +2606,18 @@ function getSettings(source = process.env) {
2541
2606
  ),
2542
2607
  openaiReasoningEffort: optional("OPENGENI_OPENAI_REASONING_EFFORT"),
2543
2608
  openaiAllowedReasoningEfforts: optional("OPENGENI_OPENAI_ALLOWED_REASONING_EFFORTS"),
2609
+ creditsDefaultModel: optional("OPENGENI_CREDITS_DEFAULT_MODEL"),
2610
+ creditsDefaultReasoningEffort: optional("OPENGENI_CREDITS_DEFAULT_REASONING_EFFORT"),
2544
2611
  openaiResponsesTransport: optional("OPENGENI_OPENAI_RESPONSES_TRANSPORT"),
2545
2612
  openaiProviderItemIds: optional("OPENGENI_OPENAI_PROVIDER_ITEM_IDS"),
2546
2613
  openaiReasoningEncryptedContent: optional("OPENGENI_OPENAI_REASONING_ENCRYPTED_CONTENT"),
2547
2614
  openaiMaxRetries: optional("OPENGENI_OPENAI_MAX_RETRIES"),
2548
2615
  webSearchEnabled: optional("OPENGENI_WEB_SEARCH_ENABLED"),
2616
+ jevApiKey: optional("OPENGENI_JEV_API_KEY"),
2617
+ jevBaseUrl: optional("OPENGENI_JEV_BASE_URL"),
2618
+ jevModel: optional("OPENGENI_JEV_MODEL"),
2619
+ jevRequestTimeoutMs: optional("OPENGENI_JEV_REQUEST_TIMEOUT_MS"),
2620
+ codeSearchMode: optional("OPENGENI_CODE_SEARCH_MODE"),
2549
2621
  agentInstructionsTemplate: optional("OPENGENI_AGENT_INSTRUCTIONS_TEMPLATE"),
2550
2622
  azureOpenaiBaseUrl: optional("OPENGENI_AZURE_OPENAI_BASE_URL"),
2551
2623
  azureOpenaiEndpoint: optional("OPENGENI_AZURE_OPENAI_ENDPOINT"),
@@ -4900,8 +4972,61 @@ function isDigestPinnedModalDesktopImage(settings) {
4900
4972
  if (settings.modalImageId) return true;
4901
4973
  return typeof settings.modalImageRef === "string" && MODAL_DESKTOP_IMAGE_DIGEST_REF.test(settings.modalImageRef);
4902
4974
  }
4975
+ function trustedProxyCidrEntries(raw) {
4976
+ const entries = [];
4977
+ for (const entry of raw.split(",")) {
4978
+ const value = entry.trim();
4979
+ if (!value) continue;
4980
+ const slash = value.indexOf("/");
4981
+ const address = slash < 0 ? value : value.slice(0, slash);
4982
+ const version = isIP(address);
4983
+ const family = version === 4 ? "ipv4" : version === 6 ? "ipv6" : null;
4984
+ const maxPrefix = family === "ipv4" ? 32 : 128;
4985
+ const prefixText = slash < 0 ? String(maxPrefix) : value.slice(slash + 1);
4986
+ const prefix = /^\d{1,3}$/u.test(prefixText) ? Number(prefixText) : Number.NaN;
4987
+ if (!family || !Number.isInteger(prefix) || prefix < 0 || prefix > maxPrefix) {
4988
+ throw new Error(
4989
+ `OPENGENI_API_TRUSTED_PROXY_CIDRS entry "${value.slice(0, 64)}" is not an IP address or CIDR range`
4990
+ );
4991
+ }
4992
+ entries.push({ address, prefix, family });
4993
+ }
4994
+ return entries;
4995
+ }
4996
+ function localAllowedOriginEntries(raw) {
4997
+ const origins = [];
4998
+ for (const entry of (raw ?? "").split(",")) {
4999
+ const value = entry.trim();
5000
+ if (!value) continue;
5001
+ let url = null;
5002
+ try {
5003
+ url = new URL(value);
5004
+ } catch {
5005
+ url = null;
5006
+ }
5007
+ if (!url || url.protocol !== "http:" && url.protocol !== "https:" || url.username || url.password || url.hostname.includes("*") || url.pathname !== "/" || url.search || url.hash || value.endsWith("?") || value.endsWith("#")) {
5008
+ throw new Error(
5009
+ `OPENGENI_LOCAL_ALLOWED_ORIGINS entry "${value.slice(0, 64)}" is not an exact http(s) origin such as http://127.0.0.1:5173`
5010
+ );
5011
+ }
5012
+ origins.push(url.origin);
5013
+ }
5014
+ return origins;
5015
+ }
5016
+ var LOCAL_SANDBOX_API_ROUTES = [
5017
+ // Codemode calls, journal reads, catalog, and the Site/SDK proxy.
5018
+ /^\/v1\/workspaces\/[^/]+\/codemode(?:\/|$)/u,
5019
+ // First-party MCP, including its /docs and /files servers.
5020
+ /^\/v1\/workspaces\/[^/]+\/mcp(?:\/|$)/u,
5021
+ // The personal GitHub HTTPS smart-Git broker.
5022
+ /^\/v1\/git\/personal\/[A-Za-z0-9_-]{43}\/(?:info\/refs|git-upload-pack|git-receive-pack)$/u
5023
+ ];
5024
+ function localSandboxApiRouteAllowed(pathname) {
5025
+ return LOCAL_SANDBOX_API_ROUTES.some((route) => route.test(pathname));
5026
+ }
4903
5027
  function validateSettings(settings, source = process.env) {
4904
5028
  temporalConnectionOptions(settings);
5029
+ localAllowedOriginEntries(settings.localAllowedOrigins);
4905
5030
  if (settings.organizationUserSetupEmailTokenTransport === "query" && !settings.organizationUserSetupQueryEdgeSanitizationConfirmed) {
4906
5031
  throw new Error(
4907
5032
  "OPENGENI_ORGANIZATION_USER_SETUP_QUERY_EDGE_SANITIZATION_CONFIRMED=true is required when OPENGENI_ORGANIZATION_USER_SETUP_EMAIL_TOKEN_TRANSPORT=query"
@@ -4984,6 +5109,18 @@ function validateSettings(settings, source = process.env) {
4984
5109
  );
4985
5110
  }
4986
5111
  }
5112
+ const retiredTrustedProxyHops = source.OPENGENI_MCP_OAUTH_TRUSTED_PROXY_HOPS?.trim();
5113
+ if (retiredTrustedProxyHops && retiredTrustedProxyHops !== "0") {
5114
+ throw new Error(
5115
+ "OPENGENI_MCP_OAUTH_TRUSTED_PROXY_HOPS was renamed to OPENGENI_API_TRUSTED_PROXY_HOPS, which now sets the client address for every API rate limit and auth session; rename the variable"
5116
+ );
5117
+ }
5118
+ const trustedProxyCidrs = trustedProxyCidrEntries(settings.apiTrustedProxyCidrs);
5119
+ if (trustedProxyCidrs.length > 0 && settings.apiTrustedProxyHops === 0) {
5120
+ throw new Error(
5121
+ "OPENGENI_API_TRUSTED_PROXY_CIDRS requires OPENGENI_API_TRUSTED_PROXY_HOPS greater than 0"
5122
+ );
5123
+ }
4987
5124
  if (settings.mcpOauthEnabled) {
4988
5125
  if (settings.productAccessMode === "configured") {
4989
5126
  throw new Error(
@@ -5432,6 +5569,16 @@ function validateModelCatalogSettings(settings, source = process.env) {
5432
5569
  `The default model ${settings.openaiModel} is not executable in the resolved model catalog`
5433
5570
  );
5434
5571
  }
5572
+ const explicitCreditsDefault = source.OPENGENI_CREDITS_DEFAULT_MODEL?.trim();
5573
+ if (explicitCreditsDefault && settings.modelCatalogSource === "code" && settings.billingMode === "stripe") {
5574
+ const creditsDefaultId = canonicalizeConfiguredModelId(settings, settings.creditsDefaultModel);
5575
+ const creditsDefault = models.find((model) => model.id === creditsDefaultId);
5576
+ if (creditsDefault?.cost !== "credits") {
5577
+ throw new Error(
5578
+ `OPENGENI_CREDITS_DEFAULT_MODEL ${settings.creditsDefaultModel} is not a credits-billed model in the resolved model catalog`
5579
+ );
5580
+ }
5581
+ }
5435
5582
  const deploymentProductIds = new Set(
5436
5583
  models.filter((model) => model.credentialSource.kind === "deployment").map((model) => model.id)
5437
5584
  );
@@ -5687,6 +5834,7 @@ export {
5687
5834
  calculateVideoGenerationCreditCostMicros,
5688
5835
  canonicalPublicOrigin,
5689
5836
  canonicalizeConfiguredModelId,
5837
+ codeSearchDeploymentPolicy,
5690
5838
  codemodeWorkspaceUrl,
5691
5839
  collectGitIdentityEnvironment,
5692
5840
  collectSandboxEnvironment,
@@ -5727,8 +5875,11 @@ export {
5727
5875
  hasGitCredentialRepositorySelection,
5728
5876
  hasGitHubRepositorySelection,
5729
5877
  isDirectOpenAiApiBaseUrl,
5878
+ isManagedOpenRouterFreeRoute,
5730
5879
  isModelAvailableForNewSelection,
5731
5880
  isUsableVoiceInputSecret,
5881
+ localAllowedOriginEntries,
5882
+ localSandboxApiRouteAllowed,
5732
5883
  modelCostClassForConfiguredModel,
5733
5884
  parseExposedPorts,
5734
5885
  parseIntegrationsOauthClientsJson,
@@ -5777,6 +5928,8 @@ export {
5777
5928
  startupRetryOptions,
5778
5929
  streamTokenDegraded,
5779
5930
  temporalConnectionOptions,
5931
+ trustedProxyCidrEntries,
5932
+ usableJevApiKey,
5780
5933
  validateModelCatalogSettings,
5781
5934
  voiceInputDeploymentConfigured,
5782
5935
  withCodexCatalogProvider,