@resolveio/server-lib 22.1.31 → 22.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.
@@ -88,8 +88,9 @@ var simpl_schema_1 = require("simpl-schema");
88
88
  var user_collection_1 = require("../collections/user.collection");
89
89
  var openai_usage_ledger_manager_1 = require("../managers/openai-usage-ledger.manager");
90
90
  var resolveio_server_app_1 = require("../resolveio-server-app");
91
- var openai_client_1 = require("../services/openai-client");
91
+ var codex_client_1 = require("../services/codex-client");
92
92
  var common_1 = require("../util/common");
93
+ var tokenizer_1 = require("../util/tokenizer");
93
94
  var DEFAULT_LIMIT = 100;
94
95
  var MAX_LIMIT = 2000;
95
96
  var ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
@@ -140,14 +141,6 @@ function normalizeExplorerMode(mode) {
140
141
  }
141
142
  return 'aicoder';
142
143
  }
143
- function isAdminUser(user) {
144
- var _a;
145
- if (!user) {
146
- return false;
147
- }
148
- var username = String(user.username || '').trim().toLowerCase();
149
- return username === 'admin' || ((_a = user.roles) === null || _a === void 0 ? void 0 : _a.super_admin) === true;
150
- }
151
144
  function allowUnschemaizedWrites() {
152
145
  return process.env.MONGO_EXPLORER_ALLOW_UNSCHEMATIZED_WRITE === 'true';
153
146
  }
@@ -403,8 +396,8 @@ function ensureWriteAccess(context, permissionView, mode) {
403
396
  throw new Error('Mongo Explorer: Readonly user');
404
397
  }
405
398
  normalizedMode = normalizeExplorerMode(mode);
406
- if (normalizedMode === 'resolveio' && !isAdminUser(user)) {
407
- throw new Error('Mongo Explorer: ResolveIO mode is read only for non-admin users');
399
+ if (normalizedMode === 'resolveio') {
400
+ throw new Error('Mongo Explorer: ResolveIO mode is read only');
408
401
  }
409
402
  allowedUsers = parseMongoExplorerWriteUsers();
410
403
  if (allowedUsers.length) {
@@ -737,7 +730,7 @@ function normalizeRiskReview(operation, payload, model, requestId) {
737
730
  ? Math.max(0, Math.min(1, normalizedConfidence))
738
731
  : 0.6;
739
732
  var summary = normalizeOptionalString(payload === null || payload === void 0 ? void 0 : payload.summary)
740
- || "Codex review marked this operation as ".concat(riskLevel, " risk.");
733
+ || "AI review marked this operation as ".concat(riskLevel, " risk.");
741
734
  var reasons = normalizeRiskList(payload === null || payload === void 0 ? void 0 : payload.reasons);
742
735
  var suggestedChecks = normalizeRiskList(payload === null || payload === void 0 ? void 0 : payload.suggested_checks);
743
736
  var shouldBlock = (payload === null || payload === void 0 ? void 0 : payload.should_block) === true || riskLevel === 'critical';
@@ -791,6 +784,26 @@ function resolveRiskReviewSettings() {
791
784
  retryDelayMs: retryDelayMs
792
785
  };
793
786
  }
787
+ function buildCodexPrompt(systemPrompt, userPrompt) {
788
+ return "System:\n".concat(systemPrompt, "\n\nUser:\n").concat(userPrompt).trim();
789
+ }
790
+ function buildCodexClient(settings) {
791
+ var fallbackModels = [];
792
+ var fallback = normalizeOptionalString(settings.fallbackModel);
793
+ if (fallback && fallback !== settings.model) {
794
+ fallbackModels.push(fallback);
795
+ }
796
+ return new codex_client_1.CodexClient(__assign(__assign({ apiKey: settings.apiKey, baseUrl: settings.baseUrl, model: settings.model }, (fallbackModels.length ? { fallbackModel: fallbackModels[0], fallbackModels: fallbackModels } : {})), { maxRetries: (0, common_1.round)(settings.maxRetries || 0), retryDelayMs: (0, common_1.round)(settings.retryDelayMs || 0) }));
797
+ }
798
+ function estimateCodexUsage(messages, responseText, model) {
799
+ var inputTokens = (0, tokenizer_1.countChatTokens)(messages, model);
800
+ var outputTokens = (0, tokenizer_1.countTokens)(responseText || '', model);
801
+ return {
802
+ inputTokens: inputTokens,
803
+ outputTokens: outputTokens,
804
+ totalTokens: inputTokens + outputTokens
805
+ };
806
+ }
794
807
  function buildRiskReviewPrompt(input) {
795
808
  var payload = {
796
809
  database: input.database,
@@ -811,7 +824,7 @@ function buildRiskReviewPrompt(input) {
811
824
  }
812
825
  function reviewOperationRisk(input) {
813
826
  return __awaiter(this, void 0, void 0, function () {
814
- var settings, client, systemPrompt, response, payload, err_1, detail;
827
+ var settings, client, systemPrompt, userPrompt, prompt, responseText, payload, err_1, detail;
815
828
  return __generator(this, function (_a) {
816
829
  switch (_a.label) {
817
830
  case 0:
@@ -820,19 +833,9 @@ function reviewOperationRisk(input) {
820
833
  return [2 /*return*/, buildDisabledRiskReview(input.operation)];
821
834
  }
822
835
  if (!settings.apiKey) {
823
- return [2 /*return*/, buildFallbackRiskReview(input.operation, 'AI risk review unavailable: OPENAI_API_KEY is missing.')];
836
+ return [2 /*return*/, buildFallbackRiskReview(input.operation, 'AI risk review unavailable: AI API key is missing.')];
824
837
  }
825
- client = new openai_client_1.OpenAIClient({
826
- apiKey: settings.apiKey,
827
- baseUrl: settings.baseUrl,
828
- model: settings.model,
829
- fallbackModel: settings.fallbackModel,
830
- temperature: 0.1,
831
- maxTokens: settings.maxTokens,
832
- maxRetries: (0, common_1.round)(settings.maxRetries),
833
- retryDelayMs: (0, common_1.round)(settings.retryDelayMs),
834
- responseFormat: 'json'
835
- });
838
+ client = buildCodexClient(settings);
836
839
  systemPrompt = [
837
840
  'You are a MongoDB operation safety reviewer for a production SaaS application.',
838
841
  'Respond with a single JSON object only.',
@@ -845,20 +848,27 @@ function reviewOperationRisk(input) {
845
848
  'confidence (number between 0 and 1).',
846
849
  'Keep summary concise (<= 220 chars).'
847
850
  ].join(' ');
851
+ userPrompt = buildRiskReviewPrompt(input);
852
+ prompt = buildCodexPrompt(systemPrompt, userPrompt);
848
853
  _a.label = 1;
849
854
  case 1:
850
855
  _a.trys.push([1, 3, , 4]);
851
- return [4 /*yield*/, client.chat([
852
- { role: 'system', content: systemPrompt },
853
- { role: 'user', content: buildRiskReviewPrompt(input) }
854
- ], {
856
+ return [4 /*yield*/, client.run(prompt, {
855
857
  timeoutMs: (0, common_1.round)(settings.timeoutMs),
856
- responseFormat: 'json'
858
+ threadOptions: {
859
+ model: settings.model,
860
+ sandboxMode: 'read-only',
861
+ skipGitRepoCheck: true,
862
+ networkAccessEnabled: false,
863
+ webSearchMode: 'disabled',
864
+ webSearchEnabled: false,
865
+ approvalPolicy: 'never'
866
+ }
857
867
  })];
858
868
  case 2:
859
- response = _a.sent();
860
- payload = parseRiskReviewPayload(response.content);
861
- return [2 /*return*/, normalizeRiskReview(input.operation, payload, response.model || settings.model, response.requestId || '')];
869
+ responseText = _a.sent();
870
+ payload = parseRiskReviewPayload(responseText);
871
+ return [2 /*return*/, normalizeRiskReview(input.operation, payload, settings.model, '')];
862
872
  case 3:
863
873
  err_1 = _a.sent();
864
874
  detail = (err_1 === null || err_1 === void 0 ? void 0 : err_1.message) ? String(err_1.message) : 'Unknown AI review error';
@@ -1155,7 +1165,7 @@ function resolveUsageClientId(idClientInput, idUser) {
1155
1165
  }
1156
1166
  function executeMongoExplorerAi(payload, context) {
1157
1167
  return __awaiter(this, void 0, void 0, function () {
1158
- var input, prompt, database, db, availableCollections, listed, selectedCollection, availableFields, settings, maxResults, client, response, parsed, action, collection, plan, removedRestrictedStages, optionsRaw, aggregateLimit, pipelineResult, aggregateOptions, aggregateRows, rows, query, options, findOptions, rows, total, _a, usage, idClient;
1168
+ var input, prompt, database, db, availableCollections, listed, selectedCollection, availableFields, settings, maxResults, client, messages, responseText, parsed, action, collection, plan, removedRestrictedStages, optionsRaw, aggregateLimit, pipelineResult, aggregateOptions, aggregateRows, rows, query, options, findOptions, rows, total, _a, usage, idClient;
1159
1169
  var _b;
1160
1170
  return __generator(this, function (_c) {
1161
1171
  switch (_c.label) {
@@ -1188,39 +1198,38 @@ function executeMongoExplorerAi(payload, context) {
1188
1198
  }
1189
1199
  settings = resolveMongoExplorerAiSettings();
1190
1200
  if (!settings.apiKey) {
1191
- throw new Error('OpenAI API key missing. Add OPENAI_API_KEY to server config.');
1201
+ throw new Error('AI API key missing. Add an AI API key to server config.');
1192
1202
  }
1193
1203
  maxResults = normalizeAiResultLimit(input.max_results, 100);
1194
- client = new openai_client_1.OpenAIClient({
1195
- apiKey: settings.apiKey,
1196
- baseUrl: settings.baseUrl,
1197
- model: settings.model,
1198
- fallbackModel: settings.fallbackModel,
1199
- temperature: 0.1,
1200
- maxTokens: (0, common_1.round)(settings.maxTokens),
1201
- maxRetries: (0, common_1.round)(settings.maxRetries),
1202
- retryDelayMs: (0, common_1.round)(settings.retryDelayMs),
1203
- responseFormat: 'json'
1204
- });
1205
- return [4 /*yield*/, client.chat([
1206
- { role: 'system', content: buildMongoExplorerAiSystemPrompt() },
1207
- {
1208
- role: 'user',
1209
- content: buildMongoExplorerAiUserPrompt({
1210
- prompt: prompt,
1211
- selectedCollection: selectedCollection,
1212
- availableCollections: availableCollections,
1213
- availableFields: availableFields,
1214
- maxResults: maxResults
1215
- })
1216
- }
1217
- ], {
1204
+ client = buildCodexClient(settings);
1205
+ messages = [
1206
+ { role: 'system', content: buildMongoExplorerAiSystemPrompt() },
1207
+ {
1208
+ role: 'user',
1209
+ content: buildMongoExplorerAiUserPrompt({
1210
+ prompt: prompt,
1211
+ selectedCollection: selectedCollection,
1212
+ availableCollections: availableCollections,
1213
+ availableFields: availableFields,
1214
+ maxResults: maxResults
1215
+ })
1216
+ }
1217
+ ];
1218
+ return [4 /*yield*/, client.run(buildCodexPrompt(messages[0].content, messages[1].content), {
1218
1219
  timeoutMs: (0, common_1.round)(settings.timeoutMs),
1219
- responseFormat: 'json'
1220
+ threadOptions: {
1221
+ model: settings.model,
1222
+ sandboxMode: 'read-only',
1223
+ skipGitRepoCheck: true,
1224
+ networkAccessEnabled: false,
1225
+ webSearchMode: 'disabled',
1226
+ webSearchEnabled: false,
1227
+ approvalPolicy: 'never'
1228
+ }
1220
1229
  })];
1221
1230
  case 3:
1222
- response = _c.sent();
1223
- parsed = parseAiPlanPayload(response.content);
1231
+ responseText = _c.sent();
1232
+ parsed = parseAiPlanPayload(responseText);
1224
1233
  action = normalizeAiAction((parsed === null || parsed === void 0 ? void 0 : parsed.action) || (Array.isArray(parsed === null || parsed === void 0 ? void 0 : parsed.pipeline) ? 'aggregate' : 'find'));
1225
1234
  collection = resolveCollectionFromList((parsed === null || parsed === void 0 ? void 0 : parsed.collection) || (parsed === null || parsed === void 0 ? void 0 : parsed.collection_name), availableCollections, selectedCollection || availableCollections[0]);
1226
1235
  if (!collection) {
@@ -1293,19 +1302,19 @@ function executeMongoExplorerAi(payload, context) {
1293
1302
  _c.label = 10;
1294
1303
  case 10:
1295
1304
  plan.notes = buildMongoExplorerAiNotes(parsed === null || parsed === void 0 ? void 0 : parsed.notes, plan.action, plan.collection, Array.isArray(plan.documents) ? plan.documents.length : 0, typeof plan.total === 'number' ? plan.total : null, removedRestrictedStages);
1296
- usage = response.usage || { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
1305
+ usage = estimateCodexUsage(messages, responseText, settings.model);
1297
1306
  return [4 /*yield*/, resolveUsageClientId(input.id_client, context === null || context === void 0 ? void 0 : context.id_user)];
1298
1307
  case 11:
1299
1308
  idClient = _c.sent();
1300
1309
  if (!(idClient && usage.totalTokens)) return [3 /*break*/, 13];
1301
1310
  return [4 /*yield*/, (0, openai_usage_ledger_manager_1.recordOpenAIUsage)({
1302
1311
  id_client: idClient,
1303
- model: response.model || settings.model || 'unknown',
1312
+ model: settings.model || 'unknown',
1304
1313
  input_tokens: usage.inputTokens || 0,
1305
1314
  output_tokens: usage.outputTokens || 0,
1306
1315
  total_tokens: usage.totalTokens || 0,
1307
1316
  category: 'mongo-explorer-ai',
1308
- id_request: response.requestId || ''
1317
+ id_request: ''
1309
1318
  })];
1310
1319
  case 12:
1311
1320
  _c.sent();
@@ -1313,7 +1322,7 @@ function executeMongoExplorerAi(payload, context) {
1313
1322
  case 13: return [2 /*return*/, {
1314
1323
  notes: plan.notes,
1315
1324
  plan: plan,
1316
- model: response.model || settings.model,
1325
+ model: settings.model,
1317
1326
  usage: {
1318
1327
  input_tokens: usage.inputTokens || 0,
1319
1328
  output_tokens: usage.outputTokens || 0,