@opengeni/config 0.7.22 → 0.9.1
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 +22 -0
- package/dist/index.js +91 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +124 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "OpenGeni runtime configuration: settings resolution, deployment knobs, and config validation shared across the server packages.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@opengeni/codex": "^0.2.9",
|
|
37
|
-
"@opengeni/contracts": "^0.
|
|
37
|
+
"@opengeni/contracts": "^0.28.1",
|
|
38
38
|
"zod": "^4.2.1"
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|
package/src/index.ts
CHANGED
|
@@ -218,6 +218,20 @@ const SettingsSchema = z.object({
|
|
|
218
218
|
observabilityMetricsEnabled: EnvBoolean.default(true),
|
|
219
219
|
observabilityOtlpEndpoint: z.string().url().optional(),
|
|
220
220
|
observabilityOtlpHeaders: z.string().default(""),
|
|
221
|
+
analyticsEnabled: EnvBoolean.default(false),
|
|
222
|
+
analyticsConsentRequired: EnvBoolean.default(true),
|
|
223
|
+
analyticsReoClientId: z
|
|
224
|
+
.string()
|
|
225
|
+
.max(128)
|
|
226
|
+
.regex(/^[A-Za-z0-9_-]+$/u)
|
|
227
|
+
.optional(),
|
|
228
|
+
analyticsPosthogProjectKey: z.string().min(1).max(256).optional(),
|
|
229
|
+
analyticsPosthogHost: z.string().url().max(2_048).optional(),
|
|
230
|
+
analyticsGa4MeasurementId: z
|
|
231
|
+
.string()
|
|
232
|
+
.max(32)
|
|
233
|
+
.regex(/^G-[A-Z0-9]+$/u)
|
|
234
|
+
.optional(),
|
|
221
235
|
publicBaseUrl: z.string().url().optional(),
|
|
222
236
|
// Browser origin when the web app and API use separate origins in local
|
|
223
237
|
// development. Production normally leaves this unset and uses publicBaseUrl.
|
|
@@ -269,11 +283,15 @@ const SettingsSchema = z.object({
|
|
|
269
283
|
integrationsOauthClientsJson: z.string().default("{}"),
|
|
270
284
|
slackClientId: z.string().optional(),
|
|
271
285
|
slackClientSecret: z.string().optional(),
|
|
286
|
+
slackSigningSecret: z.string().optional(),
|
|
272
287
|
googleDriveClientId: z.string().optional(),
|
|
273
288
|
googleDriveClientSecret: z.string().optional(),
|
|
274
289
|
// Undefined is meaningful: the migration boundary persists the product
|
|
275
290
|
// default of 3 when no deployment override is supplied.
|
|
276
291
|
maxNestedAgentDepth: z.coerce.number().int().nonnegative().max(MAX_NESTED_AGENT_DEPTH).optional(),
|
|
292
|
+
// Operator OAuth apps for first-party social connectors, keyed by provider
|
|
293
|
+
// id ("x", "reddit"): {"x":{"clientId":"...","clientSecret":"..."}}.
|
|
294
|
+
socialOauthClientsJson: z.string().default("{}"),
|
|
277
295
|
// Session goal guard rails. Goals are designed for runs that legitimately
|
|
278
296
|
// span days, so length is bounded by pathology detection (no-progress
|
|
279
297
|
// streaks, budget exhaustion), never by count. goalMaxAutoContinuations is
|
|
@@ -328,6 +346,9 @@ const SettingsSchema = z.object({
|
|
|
328
346
|
apiPort: z.coerce.number().int().positive().default(8000),
|
|
329
347
|
workerHttpPort: z.coerce.number().int().positive().default(8001),
|
|
330
348
|
opengeniMcpUrl: z.string().url().optional(),
|
|
349
|
+
// Origins allowed to send browser cookies cross-origin. Other origins may
|
|
350
|
+
// call the public API with bearer credentials, but never receive credentialed
|
|
351
|
+
// CORS responses.
|
|
331
352
|
corsAllowOriginRegex: z.string().default(String.raw`^https?://(localhost|127\.0\.0\.1)(:\d+)?$`),
|
|
332
353
|
openaiProvider: z.enum(["openai", "azure"]).default("openai"),
|
|
333
354
|
openaiApiKey: z.string().optional(),
|
|
@@ -1016,7 +1037,11 @@ export function resolveVoiceInputProviderRegistry(settings: Settings): VoiceInpu
|
|
|
1016
1037
|
// subscription turns. VOICE_INPUT_CODEX_EXPERIMENTAL is retained for
|
|
1017
1038
|
// back-compat docs/env but no longer gates inclusion.
|
|
1018
1039
|
if (!settings.codexSubscriptionEnabled) continue;
|
|
1019
|
-
providers.push({
|
|
1040
|
+
providers.push({
|
|
1041
|
+
id: "codex-subscription",
|
|
1042
|
+
kind: "codex-subscription",
|
|
1043
|
+
experimental: true,
|
|
1044
|
+
});
|
|
1020
1045
|
}
|
|
1021
1046
|
}
|
|
1022
1047
|
return providers;
|
|
@@ -1563,6 +1588,12 @@ export function getSettings(): Settings {
|
|
|
1563
1588
|
optional("OPENGENI_OTEL_EXPORTER_OTLP_ENDPOINT") ?? optional("OTEL_EXPORTER_OTLP_ENDPOINT"),
|
|
1564
1589
|
observabilityOtlpHeaders:
|
|
1565
1590
|
optional("OPENGENI_OTEL_EXPORTER_OTLP_HEADERS") ?? optional("OTEL_EXPORTER_OTLP_HEADERS"),
|
|
1591
|
+
analyticsEnabled: optional("OPENGENI_ANALYTICS_ENABLED"),
|
|
1592
|
+
analyticsConsentRequired: optional("OPENGENI_ANALYTICS_CONSENT_REQUIRED"),
|
|
1593
|
+
analyticsReoClientId: optional("OPENGENI_ANALYTICS_REO_CLIENT_ID"),
|
|
1594
|
+
analyticsPosthogProjectKey: optional("OPENGENI_ANALYTICS_POSTHOG_PROJECT_KEY"),
|
|
1595
|
+
analyticsPosthogHost: optional("OPENGENI_ANALYTICS_POSTHOG_HOST"),
|
|
1596
|
+
analyticsGa4MeasurementId: optional("OPENGENI_ANALYTICS_GA4_MEASUREMENT_ID"),
|
|
1566
1597
|
publicBaseUrl: optional("OPENGENI_PUBLIC_BASE_URL"),
|
|
1567
1598
|
webBaseUrl: optional("OPENGENI_WEB_BASE_URL"),
|
|
1568
1599
|
agentReleasesBaseUrl: optional("OPENGENI_AGENT_RELEASES_BASE_URL"),
|
|
@@ -1588,9 +1619,11 @@ export function getSettings(): Settings {
|
|
|
1588
1619
|
integrationsOauthClientsJson: optional("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON"),
|
|
1589
1620
|
slackClientId: optional("OPENGENI_SLACK_CLIENT_ID"),
|
|
1590
1621
|
slackClientSecret: optional("OPENGENI_SLACK_CLIENT_SECRET"),
|
|
1622
|
+
slackSigningSecret: optional("OPENGENI_SLACK_SIGNING_SECRET"),
|
|
1591
1623
|
googleDriveClientId: optional("OPENGENI_GOOGLE_DRIVE_CLIENT_ID"),
|
|
1592
1624
|
googleDriveClientSecret: optional("OPENGENI_GOOGLE_DRIVE_CLIENT_SECRET"),
|
|
1593
1625
|
maxNestedAgentDepth: optional("OPENGENI_MAX_NESTED_AGENT_DEPTH"),
|
|
1626
|
+
socialOauthClientsJson: optional("OPENGENI_SOCIAL_OAUTH_CLIENTS_JSON"),
|
|
1594
1627
|
goalMaxAutoContinuations: optional("OPENGENI_GOAL_MAX_AUTO_CONTINUATIONS"),
|
|
1595
1628
|
goalNoProgressLimit: optional("OPENGENI_GOAL_NO_PROGRESS_LIMIT"),
|
|
1596
1629
|
agentMaxModelCallsPerTurn: optional("OPENGENI_AGENT_MAX_MODEL_CALLS_PER_TURN"),
|
|
@@ -1820,6 +1853,23 @@ export function getSettings(): Settings {
|
|
|
1820
1853
|
return settings;
|
|
1821
1854
|
}
|
|
1822
1855
|
|
|
1856
|
+
const LOCAL_FIRST_PARTY_DELEGATION_SECRET = "opengeni-local-first-party-delegation-secret-v1";
|
|
1857
|
+
|
|
1858
|
+
/**
|
|
1859
|
+
* First-party session tools need a shared HMAC identity even in the unauthenticated
|
|
1860
|
+
* local product mode. A fixed local-only value is no broader than that mode's
|
|
1861
|
+
* existing access boundary, while configured and managed deployments continue to
|
|
1862
|
+
* require an operator-provided secret.
|
|
1863
|
+
*/
|
|
1864
|
+
export function resolveFirstPartyDelegationSecret(settings: Settings): string | undefined {
|
|
1865
|
+
const explicit = settings.delegationSecret?.trim();
|
|
1866
|
+
if (explicit) return explicit;
|
|
1867
|
+
return settings.productAccessMode === "local" &&
|
|
1868
|
+
(settings.environment === "local" || settings.environment === "test")
|
|
1869
|
+
? LOCAL_FIRST_PARTY_DELEGATION_SECRET
|
|
1870
|
+
: undefined;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1823
1873
|
/**
|
|
1824
1874
|
* The Modal sandbox idle timeout (seconds) the provider actually passes as
|
|
1825
1875
|
* idleTimeoutMs (sandbox-file-persistence). When the operator did not pin
|
|
@@ -2271,8 +2321,16 @@ function builtinCredentialSource(settings: Settings): CredentialSourceV1 {
|
|
|
2271
2321
|
}
|
|
2272
2322
|
|
|
2273
2323
|
function staticRequestMetadataForDigest(provider: ResolvedModelProvider): {
|
|
2274
|
-
headers: Array<{
|
|
2275
|
-
|
|
2324
|
+
headers: Array<{
|
|
2325
|
+
name: string;
|
|
2326
|
+
classification: "public" | "secret";
|
|
2327
|
+
value?: string;
|
|
2328
|
+
}>;
|
|
2329
|
+
query: Array<{
|
|
2330
|
+
name: string;
|
|
2331
|
+
classification: "public" | "secret";
|
|
2332
|
+
value?: string;
|
|
2333
|
+
}>;
|
|
2276
2334
|
} {
|
|
2277
2335
|
const publicHeaders = new Set(provider.publicDefaultHeaderNames ?? []);
|
|
2278
2336
|
const publicQuery = new Set(provider.publicDefaultQueryNames ?? []);
|
|
@@ -2450,7 +2508,10 @@ export function withCodexCatalogProvider(settings: Settings): Settings {
|
|
|
2450
2508
|
};
|
|
2451
2509
|
}),
|
|
2452
2510
|
};
|
|
2453
|
-
return {
|
|
2511
|
+
return {
|
|
2512
|
+
...settings,
|
|
2513
|
+
modelProvidersJson: JSON.stringify([...providers, provider]),
|
|
2514
|
+
};
|
|
2454
2515
|
}
|
|
2455
2516
|
|
|
2456
2517
|
/**
|
|
@@ -2656,7 +2717,9 @@ export function configuredModels(settings: Settings): ConfiguredModel[] {
|
|
|
2656
2717
|
: { contextWindowTokens: model.contextWindowTokens }),
|
|
2657
2718
|
...(model.effectiveContextWindowTokens === undefined
|
|
2658
2719
|
? {}
|
|
2659
|
-
: {
|
|
2720
|
+
: {
|
|
2721
|
+
effectiveContextWindowTokens: model.effectiveContextWindowTokens,
|
|
2722
|
+
}),
|
|
2660
2723
|
...(model.autoCompactTokenLimit === undefined
|
|
2661
2724
|
? {}
|
|
2662
2725
|
: { autoCompactTokenLimit: model.autoCompactTokenLimit }),
|
|
@@ -3435,7 +3498,9 @@ export function parseMcpServers(raw: string | undefined): unknown[] | undefined
|
|
|
3435
3498
|
return parsed;
|
|
3436
3499
|
} catch (error) {
|
|
3437
3500
|
const message = error instanceof Error ? error.message : String(error);
|
|
3438
|
-
throw new Error(`OPENGENI_MCP_SERVERS must be a JSON array: ${message}`, {
|
|
3501
|
+
throw new Error(`OPENGENI_MCP_SERVERS must be a JSON array: ${message}`, {
|
|
3502
|
+
cause: error,
|
|
3503
|
+
});
|
|
3439
3504
|
}
|
|
3440
3505
|
}
|
|
3441
3506
|
|
|
@@ -3586,6 +3651,53 @@ export function parseIntegrationsOauthClientsJson(
|
|
|
3586
3651
|
return out;
|
|
3587
3652
|
}
|
|
3588
3653
|
|
|
3654
|
+
export const SocialOAuthClientConfigSchema = z.object({
|
|
3655
|
+
clientId: z.string().min(1),
|
|
3656
|
+
clientSecret: z.string().min(1).optional(),
|
|
3657
|
+
});
|
|
3658
|
+
export type SocialOAuthClientConfig = z.infer<typeof SocialOAuthClientConfigSchema>;
|
|
3659
|
+
|
|
3660
|
+
const SOCIAL_OAUTH_PROVIDER_IDS = ["x", "reddit"] as const;
|
|
3661
|
+
|
|
3662
|
+
export function parseSocialOauthClientsJson(
|
|
3663
|
+
raw: string | undefined,
|
|
3664
|
+
): Partial<Record<(typeof SOCIAL_OAUTH_PROVIDER_IDS)[number], SocialOAuthClientConfig>> {
|
|
3665
|
+
if (!raw?.trim() || raw.trim() === "{}") {
|
|
3666
|
+
return {};
|
|
3667
|
+
}
|
|
3668
|
+
let parsed: unknown;
|
|
3669
|
+
try {
|
|
3670
|
+
parsed = JSON.parse(raw);
|
|
3671
|
+
} catch (error) {
|
|
3672
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3673
|
+
throw new Error(`OPENGENI_SOCIAL_OAUTH_CLIENTS_JSON must be valid JSON: ${message}`, {
|
|
3674
|
+
cause: error,
|
|
3675
|
+
});
|
|
3676
|
+
}
|
|
3677
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
3678
|
+
throw new Error(
|
|
3679
|
+
"OPENGENI_SOCIAL_OAUTH_CLIENTS_JSON must be a JSON object keyed by social provider id",
|
|
3680
|
+
);
|
|
3681
|
+
}
|
|
3682
|
+
const out: Partial<Record<(typeof SOCIAL_OAUTH_PROVIDER_IDS)[number], SocialOAuthClientConfig>> =
|
|
3683
|
+
{};
|
|
3684
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
3685
|
+
if (!SOCIAL_OAUTH_PROVIDER_IDS.includes(key as (typeof SOCIAL_OAUTH_PROVIDER_IDS)[number])) {
|
|
3686
|
+
throw new Error(
|
|
3687
|
+
`OPENGENI_SOCIAL_OAUTH_CLIENTS_JSON provider ${key} is not supported (expected: ${SOCIAL_OAUTH_PROVIDER_IDS.join(", ")})`,
|
|
3688
|
+
);
|
|
3689
|
+
}
|
|
3690
|
+
const result = SocialOAuthClientConfigSchema.safeParse(value);
|
|
3691
|
+
if (!result.success) {
|
|
3692
|
+
throw new Error(
|
|
3693
|
+
`OPENGENI_SOCIAL_OAUTH_CLIENTS_JSON client for ${key} is invalid: ${result.error.message}`,
|
|
3694
|
+
);
|
|
3695
|
+
}
|
|
3696
|
+
out[key as (typeof SOCIAL_OAUTH_PROVIDER_IDS)[number]] = result.data;
|
|
3697
|
+
}
|
|
3698
|
+
return out;
|
|
3699
|
+
}
|
|
3700
|
+
|
|
3589
3701
|
export function parseStaticUsageLimitsJson(raw: string): StaticUsageLimitsConfig {
|
|
3590
3702
|
if (!raw.trim() || raw.trim() === "{}") {
|
|
3591
3703
|
return {};
|
|
@@ -3821,6 +3933,11 @@ function validateSettings(settings: Settings): void {
|
|
|
3821
3933
|
);
|
|
3822
3934
|
}
|
|
3823
3935
|
if (settings.slackClientId) {
|
|
3936
|
+
if (!settings.slackSigningSecret) {
|
|
3937
|
+
throw new Error(
|
|
3938
|
+
"OPENGENI_SLACK_SIGNING_SECRET is required when the OpenGeni Slack app is configured",
|
|
3939
|
+
);
|
|
3940
|
+
}
|
|
3824
3941
|
if (!settings.publicBaseUrl) {
|
|
3825
3942
|
throw new Error(
|
|
3826
3943
|
"OPENGENI_PUBLIC_BASE_URL is required when the OpenGeni Slack app is configured",
|
|
@@ -3866,6 +3983,7 @@ function validateSettings(settings: Settings): void {
|
|
|
3866
3983
|
}
|
|
3867
3984
|
}
|
|
3868
3985
|
parseIntegrationsOauthClientsJson(settings.integrationsOauthClientsJson);
|
|
3986
|
+
parseSocialOauthClientsJson(settings.socialOauthClientsJson);
|
|
3869
3987
|
if (
|
|
3870
3988
|
settings.productAccessMode === "configured" &&
|
|
3871
3989
|
!["local", "test"].includes(settings.environment) &&
|