@cedrugs/opencode-omniauth 1.2.0

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.
@@ -0,0 +1,668 @@
1
+ import { OMNIROUTE_PROVIDER_ID, OMNIROUTE_DEFAULT_MODELS, OMNIROUTE_ENDPOINTS, log, } from './constants.js';
2
+ import { fetchModels } from './models.js';
3
+ const OMNIROUTE_PROVIDER_NAME = 'OmniRoute';
4
+ const OMNIROUTE_CHAT_PROVIDER_NPM = '@ai-sdk/openai-compatible';
5
+ const OMNIROUTE_RESPONSES_PROVIDER_NPM = '@ai-sdk/openai';
6
+ const OMNIROUTE_PROVIDER_ENV = ['OMNIROUTE_API_KEY'];
7
+ const REASONING_EFFORT_SUFFIXES = /-(thinking|reasoning|minimal|low|medium|high|max|xhigh|none)$/i;
8
+ const REASONING_CAPABLE_PATTERN = /\b(gpt-5|o3|o4|codex)/i;
9
+ const WIDELY_SUPPORTED_REASONING = /\b(gpt-5\.4|gpt-5\.3-codex|gpt-5\.2-codex)\b/i;
10
+ const RESPONSES_CHAT_FALLBACK_PATTERNS = [
11
+ /\bcu\/default\b/i,
12
+ /\bcursor\/default\b/i,
13
+ /\bclaude\b/i,
14
+ /\banthrop/i,
15
+ /\bopus\b/i,
16
+ /\bsonnet\b/i,
17
+ /\bhaiku\b/i,
18
+ /\bgemini\b/i,
19
+ /\bmlx[/-]/i,
20
+ /\bmlx-community\b/i,
21
+ /\bqwen\b/i,
22
+ ];
23
+ const RESPONSES_STRIP_FIELDS = new Set([
24
+ 'max_output_tokens',
25
+ 'max_tokens',
26
+ 'reasoningEffort',
27
+ 'textVerbosity',
28
+ 'reasoning_effort',
29
+ 'reasoningSummary',
30
+ 'reasoning_summary',
31
+ 'temperature',
32
+ ]);
33
+ const CHAT_STRIP_FIELDS = new Set([
34
+ 'reasoningSummary',
35
+ 'reasoning_summary',
36
+ ]);
37
+ export const OmniRouteAuthPlugin = async (_input) => {
38
+ return {
39
+ config: async (config) => {
40
+ const providers = config.provider ?? {};
41
+ const existingProvider = providers[OMNIROUTE_PROVIDER_ID];
42
+ const baseUrl = getBaseUrl(existingProvider?.options);
43
+ const apiMode = getApiMode(existingProvider?.options);
44
+ const providerApi = resolveProviderApi(existingProvider?.api, apiMode);
45
+ providers[OMNIROUTE_PROVIDER_ID] = {
46
+ ...existingProvider,
47
+ name: existingProvider?.name ?? OMNIROUTE_PROVIDER_NAME,
48
+ api: providerApi,
49
+ npm: existingProvider?.npm ?? getProviderNpm(apiMode),
50
+ env: existingProvider?.env ?? OMNIROUTE_PROVIDER_ENV,
51
+ options: {
52
+ ...(existingProvider?.options ?? {}),
53
+ baseURL: baseUrl,
54
+ apiMode,
55
+ },
56
+ models: existingProvider?.models && Object.keys(existingProvider.models).length > 0
57
+ ? existingProvider.models
58
+ : toProviderModels(OMNIROUTE_DEFAULT_MODELS, baseUrl, apiMode),
59
+ };
60
+ config.provider = providers;
61
+ },
62
+ auth: createAuthHook(),
63
+ };
64
+ };
65
+ function createAuthHook() {
66
+ return {
67
+ provider: OMNIROUTE_PROVIDER_ID,
68
+ methods: [
69
+ {
70
+ type: 'api',
71
+ label: 'API Key',
72
+ },
73
+ ],
74
+ loader: loadProviderOptions,
75
+ };
76
+ }
77
+ async function loadProviderOptions(getAuth, provider) {
78
+ const auth = await getAuth();
79
+ if (!auth || auth.type !== 'api') {
80
+ throw new Error("No API key available. Please run '/connect omniroute' to set up your OmniRoute connection.");
81
+ }
82
+ const config = createRuntimeConfig(provider, auth.key);
83
+ let models = [];
84
+ try {
85
+ const forceRefresh = config.refreshOnList !== false;
86
+ models = await fetchModels(config, config.apiKey, forceRefresh);
87
+ log.info(`[OmniRoute] Available models: ${models.map((model) => model.id).join(', ')}`);
88
+ }
89
+ catch (error) {
90
+ log.warn('[OmniRoute] Failed to fetch models, using defaults:', error);
91
+ models = OMNIROUTE_DEFAULT_MODELS;
92
+ }
93
+ replaceProviderModels(provider, toProviderModels(models, config.baseUrl, config.apiMode));
94
+ if (isRecord(provider.models)) {
95
+ log.info(`[OmniRoute] Provider models hydrated: ${Object.keys(provider.models).length}`);
96
+ }
97
+ return {
98
+ apiKey: config.apiKey,
99
+ baseURL: config.baseUrl,
100
+ url: getProviderUrl(config.baseUrl, config.apiMode),
101
+ fetch: createFetchInterceptor(config),
102
+ };
103
+ }
104
+ function createRuntimeConfig(provider, apiKey) {
105
+ const baseUrl = getBaseUrl(provider.options);
106
+ const modelCacheTtl = getPositiveNumber(provider.options, 'modelCacheTtl');
107
+ const refreshOnList = getBoolean(provider.options, 'refreshOnList');
108
+ const modelsDev = getModelsDevConfig(provider.options);
109
+ const modelMetadata = getModelMetadataConfig(provider.options);
110
+ return {
111
+ baseUrl,
112
+ apiKey,
113
+ apiMode: getApiMode(provider.options),
114
+ modelCacheTtl,
115
+ refreshOnList,
116
+ modelsDev,
117
+ modelMetadata,
118
+ };
119
+ }
120
+ function getProviderNpm(apiMode) {
121
+ return apiMode === 'responses'
122
+ ? OMNIROUTE_RESPONSES_PROVIDER_NPM
123
+ : OMNIROUTE_CHAT_PROVIDER_NPM;
124
+ }
125
+ function getProviderUrl(baseUrl, _apiMode) {
126
+ return baseUrl;
127
+ }
128
+ function resolveProviderApi(api, apiMode) {
129
+ if (isApiMode(api)) {
130
+ if (api !== apiMode) {
131
+ log.warn(`[OmniRoute] provider.api (${api}) and options.apiMode (${apiMode}) differ; using options.apiMode.`);
132
+ }
133
+ return apiMode;
134
+ }
135
+ if (typeof api === 'string') {
136
+ log.warn(`[OmniRoute] Unsupported provider.api value: ${api}. Using ${apiMode}.`);
137
+ }
138
+ return apiMode;
139
+ }
140
+ function getApiMode(options) {
141
+ const value = options?.apiMode;
142
+ if (value === undefined) {
143
+ return 'chat';
144
+ }
145
+ if (isApiMode(value)) {
146
+ return value;
147
+ }
148
+ log.warn(`[OmniRoute] Unsupported apiMode option: ${String(value)}. Using chat.`);
149
+ return 'chat';
150
+ }
151
+ function isApiMode(value) {
152
+ return value === 'chat' || value === 'responses';
153
+ }
154
+ function getBaseUrl(options) {
155
+ const rawBaseUrl = options?.baseURL;
156
+ if (typeof rawBaseUrl !== 'string') {
157
+ return OMNIROUTE_ENDPOINTS.BASE_URL;
158
+ }
159
+ const trimmed = rawBaseUrl.trim();
160
+ if (trimmed === '') {
161
+ return OMNIROUTE_ENDPOINTS.BASE_URL;
162
+ }
163
+ try {
164
+ const parsed = new URL(trimmed);
165
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
166
+ log.warn(`[OmniRoute] Ignoring unsupported baseURL protocol: ${parsed.protocol}`);
167
+ return OMNIROUTE_ENDPOINTS.BASE_URL;
168
+ }
169
+ return trimmed;
170
+ }
171
+ catch {
172
+ log.warn(`[OmniRoute] Ignoring invalid baseURL: ${trimmed}`);
173
+ return OMNIROUTE_ENDPOINTS.BASE_URL;
174
+ }
175
+ }
176
+ function getPositiveNumber(options, key) {
177
+ const value = options?.[key];
178
+ if (typeof value === 'number' && value > 0) {
179
+ return value;
180
+ }
181
+ return undefined;
182
+ }
183
+ function getBoolean(options, key) {
184
+ const value = options?.[key];
185
+ if (typeof value === 'boolean') {
186
+ return value;
187
+ }
188
+ return undefined;
189
+ }
190
+ function getModelsDevConfig(options) {
191
+ const raw = options?.modelsDev;
192
+ if (!isRecord(raw))
193
+ return undefined;
194
+ const enabled = typeof raw.enabled === 'boolean' ? raw.enabled : undefined;
195
+ const url = typeof raw.url === 'string' && raw.url.trim() !== '' ? raw.url.trim() : undefined;
196
+ const cacheTtl = getPositiveNumber(raw, 'cacheTtl');
197
+ const timeoutMs = getPositiveNumber(raw, 'timeoutMs');
198
+ const providerAliases = getStringRecord(raw.providerAliases);
199
+ if (enabled === undefined &&
200
+ url === undefined &&
201
+ cacheTtl === undefined &&
202
+ timeoutMs === undefined &&
203
+ providerAliases === undefined) {
204
+ return undefined;
205
+ }
206
+ return {
207
+ ...(enabled !== undefined ? { enabled } : {}),
208
+ ...(url !== undefined ? { url } : {}),
209
+ ...(cacheTtl !== undefined ? { cacheTtl } : {}),
210
+ ...(timeoutMs !== undefined ? { timeoutMs } : {}),
211
+ ...(providerAliases !== undefined ? { providerAliases } : {}),
212
+ };
213
+ }
214
+ function getModelMetadataConfig(options) {
215
+ const raw = options?.modelMetadata;
216
+ if (!raw)
217
+ return undefined;
218
+ if (Array.isArray(raw)) {
219
+ const filtered = raw.filter((item) => isRecord(item) && (typeof item.match === 'string' || coerceRegExp(item.match) !== null));
220
+ return filtered.length > 0 ? filtered : undefined;
221
+ }
222
+ if (isRecord(raw)) {
223
+ const hasAny = Object.values(raw).some((value) => isRecord(value));
224
+ return hasAny ? raw : undefined;
225
+ }
226
+ return undefined;
227
+ }
228
+ function getStringRecord(value) {
229
+ if (!isRecord(value))
230
+ return undefined;
231
+ const out = {};
232
+ for (const [key, raw] of Object.entries(value)) {
233
+ if (typeof raw !== 'string')
234
+ continue;
235
+ const trimmed = raw.trim();
236
+ if (!trimmed)
237
+ continue;
238
+ out[key] = trimmed;
239
+ }
240
+ return Object.keys(out).length > 0 ? out : undefined;
241
+ }
242
+ function isRegExp(value) {
243
+ return Object.prototype.toString.call(value) === '[object RegExp]';
244
+ }
245
+ function coerceRegExp(value) {
246
+ if (isRegExp(value))
247
+ return value;
248
+ if (!isRecord(value))
249
+ return null;
250
+ const source = value.source;
251
+ const flags = value.flags;
252
+ if (typeof source !== 'string' || typeof flags !== 'string')
253
+ return null;
254
+ try {
255
+ return new RegExp(source, flags);
256
+ }
257
+ catch {
258
+ return null;
259
+ }
260
+ }
261
+ function replaceProviderModels(provider, models) {
262
+ if (isRecord(provider.models)) {
263
+ for (const key of Object.keys(provider.models)) {
264
+ delete provider.models[key];
265
+ }
266
+ Object.assign(provider.models, models);
267
+ return;
268
+ }
269
+ provider.models = models;
270
+ }
271
+ function isRecord(value) {
272
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
273
+ }
274
+ function toProviderModels(models, baseUrl, globalApiMode) {
275
+ const entries = models.map((model) => [
276
+ model.id,
277
+ toProviderModel(model, baseUrl, globalApiMode),
278
+ ]);
279
+ return Object.fromEntries(entries);
280
+ }
281
+ function toProviderModel(model, baseUrl, globalApiMode) {
282
+ const supportsVision = model.supportsVision === true;
283
+ const supportsTools = model.supportsTools !== false;
284
+ const effectiveApiMode = getEffectiveApiModeForModel(model, globalApiMode);
285
+ const reasoning = getReasoningSupport(model);
286
+ const variants = getVariants(model, reasoning, effectiveApiMode);
287
+ const providerModel = {
288
+ id: model.id,
289
+ name: model.name || model.id,
290
+ providerID: OMNIROUTE_PROVIDER_ID,
291
+ family: getModelFamily(model.id),
292
+ release_date: '',
293
+ api: {
294
+ id: model.id,
295
+ url: getProviderUrl(baseUrl, effectiveApiMode),
296
+ npm: getProviderNpm(effectiveApiMode),
297
+ },
298
+ capabilities: {
299
+ temperature: true,
300
+ reasoning,
301
+ attachment: supportsVision,
302
+ toolcall: supportsTools,
303
+ input: {
304
+ text: true,
305
+ image: supportsVision,
306
+ audio: false,
307
+ video: false,
308
+ pdf: false,
309
+ },
310
+ output: {
311
+ text: true,
312
+ image: false,
313
+ audio: false,
314
+ video: false,
315
+ pdf: false,
316
+ },
317
+ interleaved: false,
318
+ },
319
+ cost: {
320
+ input: model.pricing?.input ?? 0,
321
+ output: model.pricing?.output ?? 0,
322
+ cache: {
323
+ read: 0,
324
+ write: 0,
325
+ },
326
+ },
327
+ limit: {
328
+ context: model.contextWindow ?? 4096,
329
+ output: model.maxTokens ?? 4096,
330
+ },
331
+ options: {},
332
+ headers: {},
333
+ status: 'active',
334
+ variants,
335
+ };
336
+ if (supportsVision) {
337
+ providerModel.modalities = {
338
+ input: ['text', 'image'],
339
+ output: ['text'],
340
+ };
341
+ }
342
+ return providerModel;
343
+ }
344
+ function getEffectiveApiModeForModel(model, globalApiMode) {
345
+ if (model.apiMode && isApiMode(model.apiMode)) {
346
+ return model.apiMode;
347
+ }
348
+ if (globalApiMode === 'responses' && !supportsResponsesApiStreaming(model.id)) {
349
+ return 'chat';
350
+ }
351
+ return globalApiMode;
352
+ }
353
+ function supportsResponsesApiStreaming(modelId) {
354
+ const normalized = modelId
355
+ .replace(REASONING_EFFORT_SUFFIXES, '')
356
+ .toLowerCase();
357
+ for (const pattern of RESPONSES_CHAT_FALLBACK_PATTERNS) {
358
+ if (pattern.test(normalized)) {
359
+ return false;
360
+ }
361
+ }
362
+ return true;
363
+ }
364
+ function getReasoningSupport(model) {
365
+ if (model.reasoning !== undefined) {
366
+ return model.reasoning;
367
+ }
368
+ if (WIDELY_SUPPORTED_REASONING.test(model.id)) {
369
+ return true;
370
+ }
371
+ return REASONING_CAPABLE_PATTERN.test(model.id);
372
+ }
373
+ function getVariants(model, reasoning, apiMode) {
374
+ if (!reasoning) {
375
+ return model.variants ?? {};
376
+ }
377
+ const embeddedVariant = getEmbeddedReasoningVariant(model.id, model);
378
+ if (embeddedVariant && !model.resetEmbeddedReasoningVariant) {
379
+ return {
380
+ default: {
381
+ ...getReasoningVariantOptions(embeddedVariant, apiMode),
382
+ },
383
+ ...(model.variants ?? {}),
384
+ };
385
+ }
386
+ const generated = {
387
+ low: getReasoningVariantOptions('low', apiMode),
388
+ medium: getReasoningVariantOptions('medium', apiMode),
389
+ high: getReasoningVariantOptions('high', apiMode),
390
+ };
391
+ return {
392
+ ...generated,
393
+ ...(model.variants ?? {}),
394
+ };
395
+ }
396
+ function getEmbeddedReasoningVariant(modelId, metadata) {
397
+ if (metadata?.resetEmbeddedReasoningVariant) {
398
+ return null;
399
+ }
400
+ const match = modelId.match(/-(low|medium|high|minimal|max|xhigh|none)$/i);
401
+ return match ? match[1].toLowerCase() : null;
402
+ }
403
+ function getReasoningVariantOptions(effort, _apiMode) {
404
+ return { reasoningEffort: effort };
405
+ }
406
+ function getModelFamily(modelId) {
407
+ const [family] = modelId.split('-');
408
+ return family || modelId;
409
+ }
410
+ function createFetchInterceptor(config) {
411
+ const baseUrl = config.baseUrl || 'http://localhost:20128/v1';
412
+ return async (input, init) => {
413
+ const url = input instanceof Request ? input.url : input.toString();
414
+ const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
415
+ const isOmniRouteRequest = url === baseUrl || url.startsWith(normalizedBaseUrl);
416
+ if (!isOmniRouteRequest) {
417
+ return fetch(input, init);
418
+ }
419
+ log.info(`[OmniRoute] Intercepting request to ${url}`);
420
+ const headers = new Headers(input instanceof Request ? input.headers : undefined);
421
+ if (init?.headers) {
422
+ const initHeaders = new Headers(init.headers);
423
+ initHeaders.forEach((value, key) => {
424
+ headers.set(key, value);
425
+ });
426
+ }
427
+ headers.set('Authorization', `Bearer ${config.apiKey}`);
428
+ headers.set('Content-Type', 'application/json');
429
+ const transformedBody = await transformRequestBody(input, init, url);
430
+ const modifiedInit = {
431
+ ...init,
432
+ headers,
433
+ ...(transformedBody !== undefined ? { body: transformedBody } : {}),
434
+ };
435
+ return fetch(input, modifiedInit);
436
+ };
437
+ }
438
+ async function transformRequestBody(input, init, url) {
439
+ if (!url.includes('/chat/completions') && !url.includes('/responses')) {
440
+ return undefined;
441
+ }
442
+ const rawBody = await getRawJsonBody(input, init);
443
+ if (!rawBody) {
444
+ return undefined;
445
+ }
446
+ let payload;
447
+ try {
448
+ payload = JSON.parse(rawBody);
449
+ }
450
+ catch {
451
+ return undefined;
452
+ }
453
+ if (!isRecord(payload)) {
454
+ return undefined;
455
+ }
456
+ const clonedPayload = structuredClone(payload);
457
+ let changed = false;
458
+ changed = normalizeReasoningPayload(clonedPayload) || changed;
459
+ if (url.includes('/chat/completions')) {
460
+ changed = normalizeChatPayload(clonedPayload) || changed;
461
+ }
462
+ if (url.includes('/responses')) {
463
+ changed = normalizeResponsesPayload(clonedPayload) || changed;
464
+ }
465
+ const schemaChanged = sanitizeToolSchemas(clonedPayload);
466
+ changed = schemaChanged || changed;
467
+ if (!changed) {
468
+ return undefined;
469
+ }
470
+ log.info('[OmniRoute] Request body transformed');
471
+ return JSON.stringify(clonedPayload);
472
+ }
473
+ function normalizeReasoningPayload(payload) {
474
+ let changed = false;
475
+ const effort = payload.reasoningEffort ??
476
+ payload.reasoning_effort ??
477
+ (isRecord(payload.reasoning) ? payload.reasoning.effort : undefined);
478
+ if (effort !== undefined && typeof effort === 'string') {
479
+ if (!isRecord(payload.reasoning)) {
480
+ payload.reasoning = {};
481
+ }
482
+ payload.reasoning.effort = effort;
483
+ changed = true;
484
+ }
485
+ if ('reasoningEffort' in payload) {
486
+ delete payload.reasoningEffort;
487
+ changed = true;
488
+ }
489
+ if ('reasoning_effort' in payload) {
490
+ delete payload.reasoning_effort;
491
+ changed = true;
492
+ }
493
+ return changed;
494
+ }
495
+ function normalizeChatPayload(payload) {
496
+ let changed = false;
497
+ for (const field of CHAT_STRIP_FIELDS) {
498
+ if (field in payload) {
499
+ delete payload[field];
500
+ changed = true;
501
+ }
502
+ }
503
+ if (!Array.isArray(payload.messages) && Array.isArray(payload.input)) {
504
+ payload.messages = normalizeChatMessagesFromInput(payload.input);
505
+ delete payload.input;
506
+ changed = true;
507
+ }
508
+ return changed;
509
+ }
510
+ function normalizeChatMessagesFromInput(input) {
511
+ const messages = [];
512
+ for (const item of input) {
513
+ if (typeof item === 'string') {
514
+ messages.push({ role: 'user', content: item });
515
+ continue;
516
+ }
517
+ if (!isRecord(item))
518
+ continue;
519
+ const role = typeof item.role === 'string' ? item.role : 'user';
520
+ const content = item.content ?? item.input_text ?? item.text;
521
+ if (content !== undefined) {
522
+ messages.push({ role, content });
523
+ }
524
+ }
525
+ return messages;
526
+ }
527
+ function normalizeResponsesPayload(payload) {
528
+ let changed = false;
529
+ for (const field of RESPONSES_STRIP_FIELDS) {
530
+ if (field in payload) {
531
+ delete payload[field];
532
+ changed = true;
533
+ }
534
+ }
535
+ return changed;
536
+ }
537
+ function sanitizeToolSchemas(payload) {
538
+ const tools = payload.tools;
539
+ if (!Array.isArray(tools) || tools.length === 0) {
540
+ return false;
541
+ }
542
+ const model = payload.model;
543
+ if (typeof model !== 'string' || !model.toLowerCase().includes('gemini')) {
544
+ return false;
545
+ }
546
+ const changed = sanitizeToolSchemaContainer(payload);
547
+ if (!changed) {
548
+ return false;
549
+ }
550
+ log.info('[OmniRoute] Sanitized Gemini tool schema keywords');
551
+ return true;
552
+ }
553
+ async function getRawJsonBody(input, init) {
554
+ if (typeof init?.body === 'string') {
555
+ return init.body;
556
+ }
557
+ if (!(input instanceof Request)) {
558
+ return undefined;
559
+ }
560
+ if (init?.body !== undefined) {
561
+ return undefined;
562
+ }
563
+ const contentType = input.headers.get('content-type');
564
+ if (!contentType || !contentType.toLowerCase().includes('application/json')) {
565
+ return undefined;
566
+ }
567
+ return input.clone().text();
568
+ }
569
+ const GEMINI_STRIP_KEYS = new Set(['$schema', 'additionalProperties']);
570
+ function sanitizeToolSchemaContainer(payload) {
571
+ const tools = payload.tools;
572
+ if (!Array.isArray(tools)) {
573
+ return false;
574
+ }
575
+ let changed = false;
576
+ for (const tool of tools) {
577
+ if (!isRecord(tool)) {
578
+ continue;
579
+ }
580
+ if (isRecord(tool.function) && isRecord(tool.function.parameters)) {
581
+ changed = sanitizeSchema(tool.function.parameters) || changed;
582
+ }
583
+ if (isRecord(tool.function_declaration) && isRecord(tool.function_declaration.parameters)) {
584
+ changed = sanitizeSchema(tool.function_declaration.parameters) || changed;
585
+ }
586
+ if (isRecord(tool.input_schema)) {
587
+ changed = sanitizeSchema(tool.input_schema) || changed;
588
+ }
589
+ }
590
+ return changed;
591
+ }
592
+ function sanitizeSchema(schema) {
593
+ const defs = extractDefs(schema);
594
+ return processSchemaNode(schema, defs, new Set());
595
+ }
596
+ function extractDefs(schema) {
597
+ const defs = new Map();
598
+ for (const defsKey of ['$defs', 'definitions']) {
599
+ const container = schema[defsKey];
600
+ if (!isRecord(container))
601
+ continue;
602
+ for (const [name, def] of Object.entries(container)) {
603
+ if (isRecord(def)) {
604
+ defs.set(name, def);
605
+ }
606
+ }
607
+ }
608
+ return defs;
609
+ }
610
+ function parseRefName(ref) {
611
+ const match = ref.match(/^#\/(?:\$defs|definitions)\/(.+)$/);
612
+ return match?.[1] ?? null;
613
+ }
614
+ function processSchemaNode(node, defs, resolving) {
615
+ let changed = false;
616
+ const ref = (node.$ref ?? node.ref);
617
+ if (typeof ref === 'string') {
618
+ const defName = parseRefName(ref);
619
+ if (defName && defs.has(defName) && !resolving.has(defName)) {
620
+ resolving.add(defName);
621
+ const definition = structuredClone(defs.get(defName));
622
+ processSchemaNode(definition, defs, resolving);
623
+ resolving.delete(defName);
624
+ const savedDesc = node.description;
625
+ const savedDefault = node.default;
626
+ for (const key of Object.keys(node)) {
627
+ delete node[key];
628
+ }
629
+ Object.assign(node, definition);
630
+ if (savedDesc !== undefined)
631
+ node.description = savedDesc;
632
+ if (savedDefault !== undefined)
633
+ node.default = savedDefault;
634
+ }
635
+ else {
636
+ delete node.$ref;
637
+ delete node.ref;
638
+ }
639
+ changed = true;
640
+ }
641
+ for (const defsKey of ['$defs', 'definitions']) {
642
+ if (defsKey in node) {
643
+ delete node[defsKey];
644
+ changed = true;
645
+ }
646
+ }
647
+ for (const key of Object.keys(node)) {
648
+ if (GEMINI_STRIP_KEYS.has(key)) {
649
+ delete node[key];
650
+ changed = true;
651
+ continue;
652
+ }
653
+ const value = node[key];
654
+ if (Array.isArray(value)) {
655
+ for (const item of value) {
656
+ if (isRecord(item)) {
657
+ changed = processSchemaNode(item, defs, resolving) || changed;
658
+ }
659
+ }
660
+ continue;
661
+ }
662
+ if (isRecord(value)) {
663
+ changed = processSchemaNode(value, defs, resolving) || changed;
664
+ }
665
+ }
666
+ return changed;
667
+ }
668
+ //# sourceMappingURL=plugin.js.map