@ganju/utils 0.0.8 → 0.0.9

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.
@@ -362,6 +362,9 @@ export declare const constants: {
362
362
  model: string;
363
363
  label: string;
364
364
  }[];
365
+ LLM_CATALOG_CUSTOM_KEY: string;
366
+ LLM_BASE_URL_REQUIRED_MESSAGE: string;
367
+ LLM_BASE_URL_INVALID_MESSAGE: string;
365
368
  LLM_SYSTEM_DEFAULT: string;
366
369
  MAX_TOOL_LOOPS: number;
367
370
  CHANNEL_HISTORY_LIMIT: number;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"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"}
package/dist/constants.js CHANGED
@@ -496,8 +496,8 @@ const DEFAULT_LLM_SYSTEM_PROMPT = 'You are a helpful assistant. Answer the user
496
496
  const LLM_CATALOG = [
497
497
  {
498
498
  provider: LLM_PROVIDER_GOOGLE,
499
- model: 'gemini-3-flash-preview',
500
- label: 'Gemini 3 Flash'
499
+ model: 'gemini-3.8-flash',
500
+ label: 'Gemini 3.8 Flash'
501
501
  },
502
502
  {
503
503
  provider: LLM_PROVIDER_GOOGLE,
@@ -506,13 +506,13 @@ const LLM_CATALOG = [
506
506
  },
507
507
  {
508
508
  provider: LLM_PROVIDER_OPENAI,
509
- model: 'gpt-4o-mini',
510
- label: 'GPT-4o mini'
509
+ model: 'gpt-5.4-mini',
510
+ label: 'GPT-5.4 mini'
511
511
  },
512
512
  {
513
513
  provider: LLM_PROVIDER_OPENAI,
514
- model: 'gpt-4o',
515
- label: 'GPT-4o'
514
+ model: 'gpt-5.5',
515
+ label: 'GPT-5.5'
516
516
  },
517
517
  {
518
518
  provider: LLM_PROVIDER_ANTHROPIC,
@@ -521,15 +521,30 @@ const LLM_CATALOG = [
521
521
  },
522
522
  {
523
523
  provider: LLM_PROVIDER_ANTHROPIC,
524
- model: 'claude-sonnet-4-6',
525
- label: 'Claude Sonnet 4.6'
524
+ model: 'claude-sonnet-5',
525
+ label: 'Claude Sonnet 5'
526
526
  },
527
527
  {
528
528
  provider: LLM_PROVIDER_ANTHROPIC,
529
- model: 'claude-opus-4.7',
530
- label: 'Claude Sonnet 4.7'
529
+ model: 'claude-opus-5',
530
+ label: 'Claude Opus 5'
531
531
  }
532
532
  ];
533
+ // The picker's escape hatch. Anything that speaks the OpenAI chat-completions
534
+ // protocol — a gateway, an aggregator, a self-hosted server — is reachable
535
+ // without a catalog row: the customer supplies the model id and the base URL,
536
+ // and the request goes out through the OpenAI adapter. Not a provider value, so
537
+ // it can never reach the database; the dashboard translates it into
538
+ // LLM_PROVIDER_OPENAI_COMPATIBLE on submit.
539
+ const LLM_CATALOG_CUSTOM_KEY = 'custom';
540
+ // Validation messages for an organization LLM's base URL. Constants rather than
541
+ // inline strings because localizeZodIssue keys its translations on the exact
542
+ // English text, so the two files have to name the same value. Both also carry a
543
+ // word matchStatus recognises ("required", "must be"): the update path re-checks
544
+ // the merged row and throws the message as a plain Error, and anything
545
+ // unrecognised there becomes an opaque 500 instead of a 400.
546
+ const LLM_BASE_URL_REQUIRED_MESSAGE = 'A base URL is required for an OpenAI-compatible model';
547
+ const LLM_BASE_URL_INVALID_MESSAGE = 'Base URL must be a public https:// address';
533
548
  const LLM_SYSTEM_DEFAULT = 'SYSTEM_DEFAULT';
534
549
  const MAX_TOOL_LOOPS = 8;
535
550
  // How many recent messages a channel turn replays as context.
@@ -1783,7 +1798,7 @@ const POLAR_SERVER_PRODUCTION = 'production';
1783
1798
  const CONSENT_DOCUMENT_TERMS = 'terms';
1784
1799
  const CONSENT_DOCUMENT_PRIVACY = 'privacy';
1785
1800
  const CONSENT_DOCUMENTS = [CONSENT_DOCUMENT_TERMS, CONSENT_DOCUMENT_PRIVACY];
1786
- const CONSENT_CURRENT_VERSION = '2026-08-31';
1801
+ const CONSENT_CURRENT_VERSION = '2026-09-10';
1787
1802
  const CONSENT_SOURCE_SIGNUP = 'signup';
1788
1803
  const CONSENT_SOURCE_REACCEPT = 'reaccept';
1789
1804
  const CONSENT_SOURCES = [CONSENT_SOURCE_SIGNUP, CONSENT_SOURCE_REACCEPT];
@@ -2151,6 +2166,9 @@ exports.constants = {
2151
2166
  DEFAULT_LLM_MODEL,
2152
2167
  DEFAULT_LLM_SYSTEM_PROMPT,
2153
2168
  LLM_CATALOG,
2169
+ LLM_CATALOG_CUSTOM_KEY,
2170
+ LLM_BASE_URL_REQUIRED_MESSAGE,
2171
+ LLM_BASE_URL_INVALID_MESSAGE,
2154
2172
  LLM_SYSTEM_DEFAULT,
2155
2173
  MAX_TOOL_LOOPS,
2156
2174
  CHANNEL_HISTORY_LIMIT,
package/dist/index.d.ts CHANGED
@@ -830,8 +830,8 @@ export declare const utils: {
830
830
  "openai-compatible": "openai-compatible";
831
831
  }>;
832
832
  apiKey: import("zod").ZodString;
833
- model: import("zod").ZodString;
834
833
  baseUrl: import("zod").ZodUnion<[import("zod").ZodOptional<import("zod").ZodURL>, import("zod").ZodLiteral<"">]>;
834
+ model: import("zod").ZodString;
835
835
  systemPrompt: import("zod").ZodOptional<import("zod").ZodString>;
836
836
  }, import("zod/v4/core").$strip>;
837
837
  ORGANIZATION_UPDATE_LLM: import("zod").ZodObject<{
@@ -861,8 +861,8 @@ export declare const utils: {
861
861
  "openai-compatible": "openai-compatible";
862
862
  }>>;
863
863
  apiKey: import("zod").ZodOptional<import("zod").ZodString>;
864
- model: import("zod").ZodOptional<import("zod").ZodString>;
865
864
  baseUrl: import("zod").ZodNullable<import("zod").ZodUnion<[import("zod").ZodOptional<import("zod").ZodURL>, import("zod").ZodLiteral<"">]>>;
865
+ model: import("zod").ZodOptional<import("zod").ZodString>;
866
866
  systemPrompt: import("zod").ZodNullable<import("zod").ZodOptional<import("zod").ZodString>>;
867
867
  }, import("zod/v4/core").$strip>;
868
868
  ORGANIZATION_REMOVE_LLM: import("zod").ZodObject<{
@@ -2071,6 +2071,9 @@ export declare const utils: {
2071
2071
  model: string;
2072
2072
  label: string;
2073
2073
  }[];
2074
+ LLM_CATALOG_CUSTOM_KEY: string;
2075
+ LLM_BASE_URL_REQUIRED_MESSAGE: string;
2076
+ LLM_BASE_URL_INVALID_MESSAGE: string;
2074
2077
  LLM_SYSTEM_DEFAULT: string;
2075
2078
  MAX_TOOL_LOOPS: number;
2076
2079
  CHANNEL_HISTORY_LIMIT: number;
@@ -2428,6 +2431,9 @@ export declare const utils: {
2428
2431
  projectPathIssue: (path: string, taken?: Iterable<string>) => ProjectPathIssue | null;
2429
2432
  ipv4InPrivateRange: (host: string) => boolean;
2430
2433
  isBlockedHost: (hostname: string) => boolean;
2434
+ requiresLlmBaseUrl: (provider?: string | null) => boolean;
2435
+ isAllowedLlmBaseUrl: (value: string) => boolean;
2436
+ llmBaseUrlIssue: (provider?: string | null, baseUrl?: string | null) => string | null;
2431
2437
  validateMessageVariables: (messages: Array<{
2432
2438
  role: string;
2433
2439
  content: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACzB,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAyCvD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EAClC,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,8BAA8B,EAC/B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,gCAAgC,EACjC,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EAClC,MAAM,gBAAgB,CAAC;AAKxB,OAAO,EACL,UAAU,EAGX,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAU9D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAKhE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAElE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAG5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAW3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EACV,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACxB,MAAM,cAAc,CAAC;AActB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AASvD,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,YAAY,EACZ,OAAO,EACR,MAAM,eAAe,CAAC;AAiBvB,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAI1E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAErD,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,yBAAyB,EAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAM1E,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAS3B,OAAO,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC1E,OAAO,EAEL,wBAAwB,EAIzB,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAS3E,OAAO,EAAE,cAAc,EAAoB,MAAM,kBAAkB,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAvHI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAlHF,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBA4DpB,CAAD;oBACC,CAAD;;;;aAyByC,CAAC;mBAC/B,CAAC;WAAsB,CAAC;gBACf,CAAC;gBAA2B,CAAC;;;mBAuBtC,CAAC;oBAAuB,CAAA;;;;;;;;;;;;;;;kBA5DlC,CAAC;;;;;;;sBAoCgB,CAAC;;;;;;;;;;;;;;;CA2QnB,CAAC;AAEF,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,YAAY,EACZ,OAAO,EACP,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,8BAA8B,EAC9B,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EACjC,kBAAkB,EAClB,mBAAmB,EACnB,gCAAgC,EAChC,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EACjC,SAAS,EACT,SAAS,EACT,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACvB,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,yBAAyB,EACzB,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,MAAM,EACN,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACX,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aA5HI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAlHF,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBA4DpB,CAAD;oBACC,CAAD;;;;aAyByC,CAAC;mBAC/B,CAAC;WAAsB,CAAC;gBACf,CAAC;gBAA2B,CAAC;;;mBAuBtC,CAAC;oBAAuB,CAAA;;;;;;;;;;;;;;;kBA5DlC,CAAC;;;;;;;sBAoCgB,CAAC;;;;;;;;;;;;;;;CAmRnB,CAAC;AAEF,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,YAAY,EACZ,OAAO,EACP,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,8BAA8B,EAC9B,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EACjC,kBAAkB,EAClB,mBAAmB,EACnB,gCAAgC,EAChC,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EACjC,SAAS,EACT,SAAS,EACT,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACvB,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,yBAAyB,EACzB,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,MAAM,EACN,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACX,CAAC"}
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ const vendorError_1 = require("./vendorError");
26
26
  const deadline_1 = require("./deadline");
27
27
  const customCodeProject_1 = require("./customCodeProject");
28
28
  const ssrf_1 = require("./ssrf");
29
+ const llmBaseUrl_1 = require("./llmBaseUrl");
29
30
  const formatRelative_1 = require("./formatRelative");
30
31
  const formatRelativeTime_1 = require("./formatRelativeTime");
31
32
  const languageCookieDomain_1 = require("./languageCookieDomain");
@@ -110,6 +111,9 @@ exports.utils = {
110
111
  projectPathIssue: customCodeProject_1.projectPathIssue,
111
112
  ipv4InPrivateRange: ssrf_1.ipv4InPrivateRange,
112
113
  isBlockedHost: ssrf_1.isBlockedHost,
114
+ requiresLlmBaseUrl: llmBaseUrl_1.requiresLlmBaseUrl,
115
+ isAllowedLlmBaseUrl: llmBaseUrl_1.isAllowedLlmBaseUrl,
116
+ llmBaseUrlIssue: llmBaseUrl_1.llmBaseUrlIssue,
113
117
  validateMessageVariables: validateMessageVariables_1.validateMessageVariables,
114
118
  formatRelative: formatRelative_1.formatRelative,
115
119
  formatRelativeTime: formatRelativeTime_1.formatRelativeTime,
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The rules an `organization_llm.base_url` has to satisfy, in one place.
3
+ *
4
+ * Two write paths need them and neither can see the whole picture alone: the
5
+ * create schema validates a complete payload, while an update is a partial, so
6
+ * the provider may arrive in the request and the base URL may only exist on the
7
+ * stored row. Keeping the rules here means the two can't drift into disagreeing
8
+ * about what a valid row is — the same reason every other write in this package
9
+ * goes through a shared validator rather than an inline check.
10
+ */
11
+ /**
12
+ * Which providers need a base URL of their own.
13
+ *
14
+ * Every other provider has an endpoint baked into its SDK. `openai-compatible`
15
+ * has none: it is the escape hatch for services that merely speak the OpenAI
16
+ * protocol, so without a base URL the adapter would send the customer's key to
17
+ * `api.openai.com` and fail somewhere far from the form they typed it into.
18
+ */
19
+ export declare const requiresLlmBaseUrl: (provider?: string | null) => boolean;
20
+ /**
21
+ * Whether a base URL is safe to send a request to.
22
+ *
23
+ * This is an outbound fetch target chosen by a customer, on a worker holding
24
+ * their decrypted API key, so it gets the same screening as every other
25
+ * caller-supplied URL: `https` only, because the key travels in a header, and
26
+ * no loopback, private or link-local host. The Workers runtime can't resolve
27
+ * DNS, so — as everywhere else `isBlockedHost` is used — this screens literal
28
+ * hosts and IPs and is not a defense against DNS rebinding.
29
+ */
30
+ export declare const isAllowedLlmBaseUrl: (value: string) => boolean;
31
+ /**
32
+ * Both rules against one provider/base-URL pair: the message to reject with, or
33
+ * `null` when the pair is acceptable. The messages are constants so
34
+ * `localizeZodIssue` can translate them, and both carry a word `matchStatus`
35
+ * recognises — the update path throws this as a plain Error rather than a
36
+ * ZodError, and an unrecognised message there answers 500 where it means 400.
37
+ */
38
+ export declare const llmBaseUrlIssue: (provider?: string | null, baseUrl?: string | null) => string | null;
39
+ //# sourceMappingURL=llmBaseUrl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llmBaseUrl.d.ts","sourceRoot":"","sources":["../src/llmBaseUrl.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAI,WAAW,MAAM,GAAG,IAAI,KAAG,OACP,CAAC;AAExD;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,KAAG,OASnD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAC1B,WAAW,MAAM,GAAG,IAAI,EACxB,UAAU,MAAM,GAAG,IAAI,KACtB,MAAM,GAAG,IAQX,CAAC"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.llmBaseUrlIssue = exports.isAllowedLlmBaseUrl = exports.requiresLlmBaseUrl = void 0;
4
+ const constants_1 = require("./constants");
5
+ const ssrf_1 = require("./ssrf");
6
+ /**
7
+ * The rules an `organization_llm.base_url` has to satisfy, in one place.
8
+ *
9
+ * Two write paths need them and neither can see the whole picture alone: the
10
+ * create schema validates a complete payload, while an update is a partial, so
11
+ * the provider may arrive in the request and the base URL may only exist on the
12
+ * stored row. Keeping the rules here means the two can't drift into disagreeing
13
+ * about what a valid row is — the same reason every other write in this package
14
+ * goes through a shared validator rather than an inline check.
15
+ */
16
+ /**
17
+ * Which providers need a base URL of their own.
18
+ *
19
+ * Every other provider has an endpoint baked into its SDK. `openai-compatible`
20
+ * has none: it is the escape hatch for services that merely speak the OpenAI
21
+ * protocol, so without a base URL the adapter would send the customer's key to
22
+ * `api.openai.com` and fail somewhere far from the form they typed it into.
23
+ */
24
+ const requiresLlmBaseUrl = (provider) => provider === constants_1.constants.LLM_PROVIDER_OPENAI_COMPATIBLE;
25
+ exports.requiresLlmBaseUrl = requiresLlmBaseUrl;
26
+ /**
27
+ * Whether a base URL is safe to send a request to.
28
+ *
29
+ * This is an outbound fetch target chosen by a customer, on a worker holding
30
+ * their decrypted API key, so it gets the same screening as every other
31
+ * caller-supplied URL: `https` only, because the key travels in a header, and
32
+ * no loopback, private or link-local host. The Workers runtime can't resolve
33
+ * DNS, so — as everywhere else `isBlockedHost` is used — this screens literal
34
+ * hosts and IPs and is not a defense against DNS rebinding.
35
+ */
36
+ const isAllowedLlmBaseUrl = (value) => {
37
+ let parsed;
38
+ try {
39
+ parsed = new URL(value);
40
+ }
41
+ catch {
42
+ return false;
43
+ }
44
+ if (parsed.protocol !== 'https:')
45
+ return false;
46
+ return !(0, ssrf_1.isBlockedHost)(parsed.hostname);
47
+ };
48
+ exports.isAllowedLlmBaseUrl = isAllowedLlmBaseUrl;
49
+ /**
50
+ * Both rules against one provider/base-URL pair: the message to reject with, or
51
+ * `null` when the pair is acceptable. The messages are constants so
52
+ * `localizeZodIssue` can translate them, and both carry a word `matchStatus`
53
+ * recognises — the update path throws this as a plain Error rather than a
54
+ * ZodError, and an unrecognised message there answers 500 where it means 400.
55
+ */
56
+ const llmBaseUrlIssue = (provider, baseUrl) => {
57
+ if ((0, exports.requiresLlmBaseUrl)(provider) && !baseUrl) {
58
+ return constants_1.constants.LLM_BASE_URL_REQUIRED_MESSAGE;
59
+ }
60
+ if (baseUrl && !(0, exports.isAllowedLlmBaseUrl)(baseUrl)) {
61
+ return constants_1.constants.LLM_BASE_URL_INVALID_MESSAGE;
62
+ }
63
+ return null;
64
+ };
65
+ exports.llmBaseUrlIssue = llmBaseUrlIssue;
@@ -1 +1 @@
1
- {"version":3,"file":"localizeZodIssue.d.ts","sourceRoot":"","sources":["../src/localizeZodIssue.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC;CACtB;AAkDD;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,GAAG,IAAI,KAAG,MAK3D,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,GAC3B,OAAO,YAAY,EACnB,UAAU,MAAM,KACf,MAmBF,CAAC"}
1
+ {"version":3,"file":"localizeZodIssue.d.ts","sourceRoot":"","sources":["../src/localizeZodIssue.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC;CACtB;AAwDD;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,GAAG,IAAI,KAAG,MAK3D,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,GAC3B,OAAO,YAAY,EACnB,UAAU,MAAM,KACf,MAmBF,CAAC"}
@@ -43,6 +43,12 @@ const CUSTOM_MESSAGES = {
43
43
  },
44
44
  [constants_1.constants.CUSTOM_CODE_RESOURCE_FILE_TOO_LARGE_MESSAGE]: {
45
45
  es: `Los bytes del archivo superan el límite de ${constants_1.constants.CUSTOM_CODE_MAX_RESOURCE_FILE_BYTES / (1024 * 1024)}MB`
46
+ },
47
+ [constants_1.constants.LLM_BASE_URL_REQUIRED_MESSAGE]: {
48
+ es: 'Se requiere una URL base para un modelo compatible con OpenAI'
49
+ },
50
+ [constants_1.constants.LLM_BASE_URL_INVALID_MESSAGE]: {
51
+ es: 'La URL base debe ser una dirección https:// pública'
46
52
  }
47
53
  };
48
54
  // Built once. `esLocale()` allocates its dictionaries on every call, and this
package/dist/schema.d.ts CHANGED
@@ -1215,8 +1215,8 @@ export declare const Schema: {
1215
1215
  "openai-compatible": "openai-compatible";
1216
1216
  }>;
1217
1217
  apiKey: z.ZodString;
1218
- model: z.ZodString;
1219
1218
  baseUrl: z.ZodUnion<[z.ZodOptional<z.ZodURL>, z.ZodLiteral<"">]>;
1219
+ model: z.ZodString;
1220
1220
  systemPrompt: z.ZodOptional<z.ZodString>;
1221
1221
  }, z.core.$strip>;
1222
1222
  ORGANIZATION_UPDATE_LLM: z.ZodObject<{
@@ -1246,8 +1246,8 @@ export declare const Schema: {
1246
1246
  "openai-compatible": "openai-compatible";
1247
1247
  }>>;
1248
1248
  apiKey: z.ZodOptional<z.ZodString>;
1249
- model: z.ZodOptional<z.ZodString>;
1250
1249
  baseUrl: z.ZodNullable<z.ZodUnion<[z.ZodOptional<z.ZodURL>, z.ZodLiteral<"">]>>;
1250
+ model: z.ZodOptional<z.ZodString>;
1251
1251
  systemPrompt: z.ZodNullable<z.ZodOptional<z.ZodString>>;
1252
1252
  }, z.core.$strip>;
1253
1253
  ORGANIZATION_REMOVE_LLM: z.ZodObject<{
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA6tBxB,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0FrB,CAAC;AAyDN,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;iBAmCpB,CAAC;AAWH,QAAA,MAAM,kBAAkB;;;;;;;;;;iBA+CtB,CAAC;AAiCH,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAapB,CAAC;AAYH,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgCxB,CAAC;AA4LH,QAAA,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4DpC,CAAC;AA4BL,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAkDzB,CAAC;AAwGH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0GlB,CAAC;AAGF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAG1E,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAIlE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAMtE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAGtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAMtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAIvE,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAC5C,OAAO,kCAAkC,CAC1C,CAAC;AAKF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB;AAKD,MAAM,WAAW,0BAA0B;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAKD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAC1E;AAMD,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,KAAK,EAAE,sBAAsB,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,0BAA0B,EAAE,CAAC;IACzC,OAAO,CAAC,EAAE,wBAAwB,EAAE,CAAC;CACtC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAixBxB,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0FrB,CAAC;AAyDN,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;iBAmCpB,CAAC;AAWH,QAAA,MAAM,kBAAkB;;;;;;;;;;iBA+CtB,CAAC;AAiCH,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAapB,CAAC;AAYH,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgCxB,CAAC;AA4LH,QAAA,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4DpC,CAAC;AA4BL,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAkDzB,CAAC;AAwGH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0GlB,CAAC;AAGF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAG1E,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAIlE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAMtE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAGtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAMtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAIvE,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAC5C,OAAO,kCAAkC,CAC1C,CAAC;AAKF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB;AAKD,MAAM,WAAW,0BAA0B;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAKD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAC1E;AAMD,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,KAAK,EAAE,sBAAsB,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,0BAA0B,EAAE,CAAC;IACzC,OAAO,CAAC,EAAE,wBAAwB,EAAE,CAAC;CACtC"}
package/dist/schema.js CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Schema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const constants_1 = require("./constants");
6
+ const llmBaseUrl_1 = require("./llmBaseUrl");
6
7
  const slug_1 = require("./slug");
7
8
  const reservedToolName_1 = require("./reservedToolName");
8
9
  const slugifyTitle_1 = require("./slugifyTitle");
@@ -491,29 +492,52 @@ const ORGANIZATION_LIST_LLM = zod_1.z.object({
491
492
  userId: zod_1.z.uuid(),
492
493
  organizationId: zod_1.z.uuid()
493
494
  });
494
- const ORGANIZATION_CREATE_LLM = zod_1.z.object({
495
+ // The two rules live in `llmBaseUrl.ts`, because the update controller applies
496
+ // them again to the merged row — the only place both halves are visible. Here
497
+ // they are just the field validator and the cross-field check.
498
+ const LLM_BASE_URL = zod_1.z
499
+ .url()
500
+ .max(500)
501
+ .refine(llmBaseUrl_1.isAllowedLlmBaseUrl, constants_1.constants.LLM_BASE_URL_INVALID_MESSAGE);
502
+ const BASE_URL_REQUIRED = {
503
+ message: constants_1.constants.LLM_BASE_URL_REQUIRED_MESSAGE,
504
+ path: ['baseUrl']
505
+ };
506
+ const hasBaseUrlWhenRequired = (provider, baseUrl) => !(0, llmBaseUrl_1.requiresLlmBaseUrl)(provider) || !!baseUrl;
507
+ // The base-URL rule is a cross-field check, and zod cannot `.omit()` from a
508
+ // schema that already carries one. So the shape stays a plain object — the
509
+ // `_VIEW` variants below derive from it — and the refinement is applied last,
510
+ // to each schema in turn, which also keeps the two in step.
511
+ const ORGANIZATION_CREATE_LLM_SHAPE = zod_1.z.object({
495
512
  name: zod_1.z.string().min(1).max(200),
496
513
  provider: zod_1.z.enum(constants_1.constants.LLM_PROVIDERS),
497
514
  model: zod_1.z.string().min(1).max(200),
498
- baseUrl: zod_1.z.url().optional().or(zod_1.z.literal('')),
515
+ baseUrl: LLM_BASE_URL.optional().or(zod_1.z.literal('')),
499
516
  apiKey: zod_1.z.string().min(1).max(500),
500
517
  systemPrompt: zod_1.z.string().max(10000).optional(),
501
518
  config: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
502
519
  userId: zod_1.z.uuid(),
503
520
  organizationId: zod_1.z.uuid()
504
521
  });
505
- const ORGANIZATION_UPDATE_LLM = zod_1.z.object({
522
+ const ORGANIZATION_CREATE_LLM = ORGANIZATION_CREATE_LLM_SHAPE.refine(v => hasBaseUrlWhenRequired(v.provider, v.baseUrl), BASE_URL_REQUIRED);
523
+ // An update is a partial: an omitted field keeps its stored value, so the
524
+ // base-URL rule can only be checked here when the caller sends the provider and
525
+ // the base URL together. The controller re-checks it against the merged row,
526
+ // which is the only place both halves are known.
527
+ const ORGANIZATION_UPDATE_LLM_SHAPE = zod_1.z.object({
506
528
  llmId: zod_1.z.uuid(),
507
529
  name: zod_1.z.string().min(1).max(200).optional(),
508
530
  provider: zod_1.z.enum(constants_1.constants.LLM_PROVIDERS).optional(),
509
531
  model: zod_1.z.string().min(1).max(200).optional(),
510
- baseUrl: zod_1.z.url().optional().or(zod_1.z.literal('')).nullable(),
532
+ baseUrl: LLM_BASE_URL.optional().or(zod_1.z.literal('')).nullable(),
511
533
  apiKey: zod_1.z.string().min(1).max(500).optional(),
512
534
  systemPrompt: zod_1.z.string().max(10000).optional().nullable(),
513
535
  config: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional().nullable(),
514
536
  userId: zod_1.z.uuid(),
515
537
  organizationId: zod_1.z.uuid()
516
538
  });
539
+ const refineUpdateBaseUrl = (schema) => schema.refine(v => v.baseUrl === undefined || hasBaseUrlWhenRequired(v.provider, v.baseUrl), BASE_URL_REQUIRED);
540
+ const ORGANIZATION_UPDATE_LLM = refineUpdateBaseUrl(ORGANIZATION_UPDATE_LLM_SHAPE);
517
541
  const ORGANIZATION_REMOVE_LLM = zod_1.z.object({
518
542
  llmId: zod_1.z.uuid(),
519
543
  userId: zod_1.z.uuid(),
@@ -561,15 +585,15 @@ const ACCESS_TOKEN_CREATE_VIEW = ACCESS_TOKEN_CREATE.omit({
561
585
  organizationId: true,
562
586
  projectId: true
563
587
  });
564
- const ORGANIZATION_CREATE_LLM_VIEW = ORGANIZATION_CREATE_LLM.omit({
588
+ const ORGANIZATION_CREATE_LLM_VIEW = ORGANIZATION_CREATE_LLM_SHAPE.omit({
565
589
  userId: true,
566
590
  organizationId: true
567
- });
568
- const ORGANIZATION_UPDATE_LLM_VIEW = ORGANIZATION_UPDATE_LLM.omit({
591
+ }).refine(v => hasBaseUrlWhenRequired(v.provider, v.baseUrl), BASE_URL_REQUIRED);
592
+ const ORGANIZATION_UPDATE_LLM_VIEW = refineUpdateBaseUrl(ORGANIZATION_UPDATE_LLM_SHAPE.omit({
569
593
  llmId: true,
570
594
  userId: true,
571
595
  organizationId: true
572
- });
596
+ }));
573
597
  // `channel.config` is an open bag, so this stays loose — it only pins down the
574
598
  // keys we actually read, and passes everything else through untouched.
575
599
  // `debounceMs` is how long a burst from one participant is buffered before the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ganju/utils",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "Shared constants, schemas and helpers for Ganju",
5
5
  "license": "Apache-2.0",
6
6
  "author": "MontoyaAndres <andresmontoyafcb@gmail.com>",
package/src/constants.ts CHANGED
@@ -622,8 +622,8 @@ const LLM_CATALOG: ReadonlyArray<{
622
622
  }> = [
623
623
  {
624
624
  provider: LLM_PROVIDER_GOOGLE,
625
- model: 'gemini-3-flash-preview',
626
- label: 'Gemini 3 Flash'
625
+ model: 'gemini-3.8-flash',
626
+ label: 'Gemini 3.8 Flash'
627
627
  },
628
628
  {
629
629
  provider: LLM_PROVIDER_GOOGLE,
@@ -632,13 +632,13 @@ const LLM_CATALOG: ReadonlyArray<{
632
632
  },
633
633
  {
634
634
  provider: LLM_PROVIDER_OPENAI,
635
- model: 'gpt-4o-mini',
636
- label: 'GPT-4o mini'
635
+ model: 'gpt-5.4-mini',
636
+ label: 'GPT-5.4 mini'
637
637
  },
638
638
  {
639
639
  provider: LLM_PROVIDER_OPENAI,
640
- model: 'gpt-4o',
641
- label: 'GPT-4o'
640
+ model: 'gpt-5.5',
641
+ label: 'GPT-5.5'
642
642
  },
643
643
  {
644
644
  provider: LLM_PROVIDER_ANTHROPIC,
@@ -647,16 +647,35 @@ const LLM_CATALOG: ReadonlyArray<{
647
647
  },
648
648
  {
649
649
  provider: LLM_PROVIDER_ANTHROPIC,
650
- model: 'claude-sonnet-4-6',
651
- label: 'Claude Sonnet 4.6'
650
+ model: 'claude-sonnet-5',
651
+ label: 'Claude Sonnet 5'
652
652
  },
653
653
  {
654
654
  provider: LLM_PROVIDER_ANTHROPIC,
655
- model: 'claude-opus-4.7',
656
- label: 'Claude Sonnet 4.7'
655
+ model: 'claude-opus-5',
656
+ label: 'Claude Opus 5'
657
657
  }
658
658
  ];
659
659
 
660
+ // The picker's escape hatch. Anything that speaks the OpenAI chat-completions
661
+ // protocol — a gateway, an aggregator, a self-hosted server — is reachable
662
+ // without a catalog row: the customer supplies the model id and the base URL,
663
+ // and the request goes out through the OpenAI adapter. Not a provider value, so
664
+ // it can never reach the database; the dashboard translates it into
665
+ // LLM_PROVIDER_OPENAI_COMPATIBLE on submit.
666
+ const LLM_CATALOG_CUSTOM_KEY = 'custom';
667
+
668
+ // Validation messages for an organization LLM's base URL. Constants rather than
669
+ // inline strings because localizeZodIssue keys its translations on the exact
670
+ // English text, so the two files have to name the same value. Both also carry a
671
+ // word matchStatus recognises ("required", "must be"): the update path re-checks
672
+ // the merged row and throws the message as a plain Error, and anything
673
+ // unrecognised there becomes an opaque 500 instead of a 400.
674
+ const LLM_BASE_URL_REQUIRED_MESSAGE =
675
+ 'A base URL is required for an OpenAI-compatible model';
676
+ const LLM_BASE_URL_INVALID_MESSAGE =
677
+ 'Base URL must be a public https:// address';
678
+
660
679
  const LLM_SYSTEM_DEFAULT = 'SYSTEM_DEFAULT';
661
680
 
662
681
  const MAX_TOOL_LOOPS = 8;
@@ -2128,7 +2147,7 @@ const POLAR_SERVER_PRODUCTION = 'production' as 'production';
2128
2147
  const CONSENT_DOCUMENT_TERMS = 'terms' as 'terms';
2129
2148
  const CONSENT_DOCUMENT_PRIVACY = 'privacy' as 'privacy';
2130
2149
  const CONSENT_DOCUMENTS = [CONSENT_DOCUMENT_TERMS, CONSENT_DOCUMENT_PRIVACY];
2131
- const CONSENT_CURRENT_VERSION = '2026-08-31';
2150
+ const CONSENT_CURRENT_VERSION = '2026-09-10';
2132
2151
 
2133
2152
  const CONSENT_SOURCE_SIGNUP = 'signup' as 'signup';
2134
2153
  const CONSENT_SOURCE_REACCEPT = 'reaccept' as 'reaccept';
@@ -2506,6 +2525,9 @@ export const constants = {
2506
2525
  DEFAULT_LLM_MODEL,
2507
2526
  DEFAULT_LLM_SYSTEM_PROMPT,
2508
2527
  LLM_CATALOG,
2528
+ LLM_CATALOG_CUSTOM_KEY,
2529
+ LLM_BASE_URL_REQUIRED_MESSAGE,
2530
+ LLM_BASE_URL_INVALID_MESSAGE,
2509
2531
  LLM_SYSTEM_DEFAULT,
2510
2532
  MAX_TOOL_LOOPS,
2511
2533
  CHANNEL_HISTORY_LIMIT,
package/src/index.ts CHANGED
@@ -134,6 +134,11 @@ import type {
134
134
  ResolveAttachmentResult
135
135
  } from './attachment';
136
136
  import { ipv4InPrivateRange, isBlockedHost } from './ssrf';
137
+ import {
138
+ requiresLlmBaseUrl,
139
+ isAllowedLlmBaseUrl,
140
+ llmBaseUrlIssue
141
+ } from './llmBaseUrl';
137
142
  import { formatRelative } from './formatRelative';
138
143
  import { formatRelativeTime } from './formatRelativeTime';
139
144
  import { languageCookieDomain } from './languageCookieDomain';
@@ -297,6 +302,9 @@ export const utils = {
297
302
  projectPathIssue,
298
303
  ipv4InPrivateRange,
299
304
  isBlockedHost,
305
+ requiresLlmBaseUrl,
306
+ isAllowedLlmBaseUrl,
307
+ llmBaseUrlIssue,
300
308
  validateMessageVariables,
301
309
  formatRelative,
302
310
  formatRelativeTime,
@@ -0,0 +1,65 @@
1
+ import { constants } from './constants';
2
+ import { isBlockedHost } from './ssrf';
3
+
4
+ /**
5
+ * The rules an `organization_llm.base_url` has to satisfy, in one place.
6
+ *
7
+ * Two write paths need them and neither can see the whole picture alone: the
8
+ * create schema validates a complete payload, while an update is a partial, so
9
+ * the provider may arrive in the request and the base URL may only exist on the
10
+ * stored row. Keeping the rules here means the two can't drift into disagreeing
11
+ * about what a valid row is — the same reason every other write in this package
12
+ * goes through a shared validator rather than an inline check.
13
+ */
14
+
15
+ /**
16
+ * Which providers need a base URL of their own.
17
+ *
18
+ * Every other provider has an endpoint baked into its SDK. `openai-compatible`
19
+ * has none: it is the escape hatch for services that merely speak the OpenAI
20
+ * protocol, so without a base URL the adapter would send the customer's key to
21
+ * `api.openai.com` and fail somewhere far from the form they typed it into.
22
+ */
23
+ export const requiresLlmBaseUrl = (provider?: string | null): boolean =>
24
+ provider === constants.LLM_PROVIDER_OPENAI_COMPATIBLE;
25
+
26
+ /**
27
+ * Whether a base URL is safe to send a request to.
28
+ *
29
+ * This is an outbound fetch target chosen by a customer, on a worker holding
30
+ * their decrypted API key, so it gets the same screening as every other
31
+ * caller-supplied URL: `https` only, because the key travels in a header, and
32
+ * no loopback, private or link-local host. The Workers runtime can't resolve
33
+ * DNS, so — as everywhere else `isBlockedHost` is used — this screens literal
34
+ * hosts and IPs and is not a defense against DNS rebinding.
35
+ */
36
+ export const isAllowedLlmBaseUrl = (value: string): boolean => {
37
+ let parsed: URL;
38
+ try {
39
+ parsed = new URL(value);
40
+ } catch {
41
+ return false;
42
+ }
43
+ if (parsed.protocol !== 'https:') return false;
44
+ return !isBlockedHost(parsed.hostname);
45
+ };
46
+
47
+ /**
48
+ * Both rules against one provider/base-URL pair: the message to reject with, or
49
+ * `null` when the pair is acceptable. The messages are constants so
50
+ * `localizeZodIssue` can translate them, and both carry a word `matchStatus`
51
+ * recognises — the update path throws this as a plain Error rather than a
52
+ * ZodError, and an unrecognised message there answers 500 where it means 400.
53
+ */
54
+ export const llmBaseUrlIssue = (
55
+ provider?: string | null,
56
+ baseUrl?: string | null
57
+ ): string | null => {
58
+ if (requiresLlmBaseUrl(provider) && !baseUrl) {
59
+ return constants.LLM_BASE_URL_REQUIRED_MESSAGE;
60
+ }
61
+ if (baseUrl && !isAllowedLlmBaseUrl(baseUrl)) {
62
+ return constants.LLM_BASE_URL_INVALID_MESSAGE;
63
+ }
64
+ return null;
65
+ };
@@ -74,6 +74,12 @@ const CUSTOM_MESSAGES: Record<string, Record<string, string>> = {
74
74
  },
75
75
  [constants.CUSTOM_CODE_RESOURCE_FILE_TOO_LARGE_MESSAGE]: {
76
76
  es: `Los bytes del archivo superan el límite de ${constants.CUSTOM_CODE_MAX_RESOURCE_FILE_BYTES / (1024 * 1024)}MB`
77
+ },
78
+ [constants.LLM_BASE_URL_REQUIRED_MESSAGE]: {
79
+ es: 'Se requiere una URL base para un modelo compatible con OpenAI'
80
+ },
81
+ [constants.LLM_BASE_URL_INVALID_MESSAGE]: {
82
+ es: 'La URL base debe ser una dirección https:// pública'
77
83
  }
78
84
  };
79
85
 
package/src/schema.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  import { constants } from './constants';
4
+ import { isAllowedLlmBaseUrl, requiresLlmBaseUrl } from './llmBaseUrl';
4
5
  import { isReservedSlug, isValidSlugFormat } from './slug';
5
6
  import { isReservedToolName } from './reservedToolName';
6
7
  import { slugifyTitle } from './slugifyTitle';
@@ -572,11 +573,33 @@ const ORGANIZATION_LIST_LLM = z.object({
572
573
  organizationId: z.uuid()
573
574
  });
574
575
 
575
- const ORGANIZATION_CREATE_LLM = z.object({
576
+ // The two rules live in `llmBaseUrl.ts`, because the update controller applies
577
+ // them again to the merged row — the only place both halves are visible. Here
578
+ // they are just the field validator and the cross-field check.
579
+ const LLM_BASE_URL = z
580
+ .url()
581
+ .max(500)
582
+ .refine(isAllowedLlmBaseUrl, constants.LLM_BASE_URL_INVALID_MESSAGE);
583
+
584
+ const BASE_URL_REQUIRED = {
585
+ message: constants.LLM_BASE_URL_REQUIRED_MESSAGE,
586
+ path: ['baseUrl']
587
+ };
588
+
589
+ const hasBaseUrlWhenRequired = (
590
+ provider: string | undefined,
591
+ baseUrl: unknown
592
+ ) => !requiresLlmBaseUrl(provider) || !!baseUrl;
593
+
594
+ // The base-URL rule is a cross-field check, and zod cannot `.omit()` from a
595
+ // schema that already carries one. So the shape stays a plain object — the
596
+ // `_VIEW` variants below derive from it — and the refinement is applied last,
597
+ // to each schema in turn, which also keeps the two in step.
598
+ const ORGANIZATION_CREATE_LLM_SHAPE = z.object({
576
599
  name: z.string().min(1).max(200),
577
600
  provider: z.enum(constants.LLM_PROVIDERS),
578
601
  model: z.string().min(1).max(200),
579
- baseUrl: z.url().optional().or(z.literal('')),
602
+ baseUrl: LLM_BASE_URL.optional().or(z.literal('')),
580
603
  apiKey: z.string().min(1).max(500),
581
604
  systemPrompt: z.string().max(10000).optional(),
582
605
  config: z.record(z.string(), z.any()).optional(),
@@ -584,12 +607,21 @@ const ORGANIZATION_CREATE_LLM = z.object({
584
607
  organizationId: z.uuid()
585
608
  });
586
609
 
587
- const ORGANIZATION_UPDATE_LLM = z.object({
610
+ const ORGANIZATION_CREATE_LLM = ORGANIZATION_CREATE_LLM_SHAPE.refine(
611
+ v => hasBaseUrlWhenRequired(v.provider, v.baseUrl),
612
+ BASE_URL_REQUIRED
613
+ );
614
+
615
+ // An update is a partial: an omitted field keeps its stored value, so the
616
+ // base-URL rule can only be checked here when the caller sends the provider and
617
+ // the base URL together. The controller re-checks it against the merged row,
618
+ // which is the only place both halves are known.
619
+ const ORGANIZATION_UPDATE_LLM_SHAPE = z.object({
588
620
  llmId: z.uuid(),
589
621
  name: z.string().min(1).max(200).optional(),
590
622
  provider: z.enum(constants.LLM_PROVIDERS).optional(),
591
623
  model: z.string().min(1).max(200).optional(),
592
- baseUrl: z.url().optional().or(z.literal('')).nullable(),
624
+ baseUrl: LLM_BASE_URL.optional().or(z.literal('')).nullable(),
593
625
  apiKey: z.string().min(1).max(500).optional(),
594
626
  systemPrompt: z.string().max(10000).optional().nullable(),
595
627
  config: z.record(z.string(), z.any()).optional().nullable(),
@@ -597,6 +629,21 @@ const ORGANIZATION_UPDATE_LLM = z.object({
597
629
  organizationId: z.uuid()
598
630
  });
599
631
 
632
+ const refineUpdateBaseUrl = <
633
+ T extends z.ZodType<{ provider?: string; baseUrl?: unknown }>
634
+ >(
635
+ schema: T
636
+ ) =>
637
+ schema.refine(
638
+ v =>
639
+ v.baseUrl === undefined || hasBaseUrlWhenRequired(v.provider, v.baseUrl),
640
+ BASE_URL_REQUIRED
641
+ );
642
+
643
+ const ORGANIZATION_UPDATE_LLM = refineUpdateBaseUrl(
644
+ ORGANIZATION_UPDATE_LLM_SHAPE
645
+ );
646
+
600
647
  const ORGANIZATION_REMOVE_LLM = z.object({
601
648
  llmId: z.uuid(),
602
649
  userId: z.uuid(),
@@ -649,16 +696,21 @@ const ACCESS_TOKEN_CREATE_VIEW = ACCESS_TOKEN_CREATE.omit({
649
696
  projectId: true
650
697
  });
651
698
 
652
- const ORGANIZATION_CREATE_LLM_VIEW = ORGANIZATION_CREATE_LLM.omit({
699
+ const ORGANIZATION_CREATE_LLM_VIEW = ORGANIZATION_CREATE_LLM_SHAPE.omit({
653
700
  userId: true,
654
701
  organizationId: true
655
- });
702
+ }).refine(
703
+ v => hasBaseUrlWhenRequired(v.provider, v.baseUrl),
704
+ BASE_URL_REQUIRED
705
+ );
656
706
 
657
- const ORGANIZATION_UPDATE_LLM_VIEW = ORGANIZATION_UPDATE_LLM.omit({
658
- llmId: true,
659
- userId: true,
660
- organizationId: true
661
- });
707
+ const ORGANIZATION_UPDATE_LLM_VIEW = refineUpdateBaseUrl(
708
+ ORGANIZATION_UPDATE_LLM_SHAPE.omit({
709
+ llmId: true,
710
+ userId: true,
711
+ organizationId: true
712
+ })
713
+ );
662
714
 
663
715
  // `channel.config` is an open bag, so this stays loose — it only pins down the
664
716
  // keys we actually read, and passes everything else through untouched.