codexui-android 0.1.82 → 0.1.85

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-cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-UUZOL7SL.js";
2
+ import "./chunk-JQMCS7KJ.js";
3
3
 
4
4
  // src/cli/index.ts
5
5
  import { createServer as createServer2 } from "http";
@@ -227,7 +227,7 @@ import * as Sentry4 from "@sentry/node";
227
227
  import { spawn as spawn4 } from "child_process";
228
228
  import { createHash as createHash2, randomBytes } from "crypto";
229
229
  import { mkdtemp as mkdtemp3, readFile as readFile3, rm as rm4, mkdir as mkdir4, stat as stat4 } from "fs/promises";
230
- import { createReadStream } from "fs";
230
+ import { createReadStream, readFileSync } from "fs";
231
231
  import { request as httpRequest } from "http";
232
232
  import { request as httpsRequest } from "https";
233
233
  import { homedir as homedir4 } from "os";
@@ -480,6 +480,22 @@ async function validateSwitchedAccount(appServer) {
480
480
  quotaSnapshot: pickCodexRateLimitSnapshot(quotaPayload)
481
481
  };
482
482
  }
483
+ async function validateSwitchedAccountWithTimeout(appServer) {
484
+ let timeoutHandle = null;
485
+ try {
486
+ return await Promise.race([
487
+ validateSwitchedAccount(appServer),
488
+ new Promise((_, reject) => {
489
+ timeoutHandle = setTimeout(() => {
490
+ reject(new Error(`Account switch validation timed out after ${ACCOUNT_INSPECTION_TIMEOUT_MS}ms`));
491
+ }, ACCOUNT_INSPECTION_TIMEOUT_MS);
492
+ timeoutHandle.unref?.();
493
+ })
494
+ ]);
495
+ } finally {
496
+ if (timeoutHandle) clearTimeout(timeoutHandle);
497
+ }
498
+ }
483
499
  async function restoreActiveAuth(raw) {
484
500
  const path = getActiveAuthPath();
485
501
  if (raw === null) {
@@ -794,7 +810,7 @@ async function handleAccountRoutes(req, res, url, context) {
794
810
  const imported = await importAccountFromAuthPath(getActiveAuthPath());
795
811
  try {
796
812
  appServer.dispose();
797
- const inspection = await validateSwitchedAccount(appServer);
813
+ const inspection = await validateSwitchedAccountWithTimeout(appServer);
798
814
  const state = await readStoredAccountsState();
799
815
  const importedAccountId = imported.importedAccountId;
800
816
  const target = state.accounts.find((entry) => entry.accountId === importedAccountId) ?? null;
@@ -893,7 +909,7 @@ async function handleAccountRoutes(req, res, url, context) {
893
909
  await writeFile(getActiveAuthPath(), targetRaw, { encoding: "utf8", mode: 384 });
894
910
  try {
895
911
  appServer.dispose();
896
- const inspection = await validateSwitchedAccount(appServer);
912
+ const inspection = await validateSwitchedAccountWithTimeout(appServer);
897
913
  const nextEntry = {
898
914
  ...target,
899
915
  email: inspection.metadata.email ?? target.email,
@@ -1034,7 +1050,7 @@ async function handleAccountRoutes(req, res, url, context) {
1034
1050
  await writeFile(getActiveAuthPath(), replacementRaw, { encoding: "utf8", mode: 384 });
1035
1051
  try {
1036
1052
  appServer.dispose();
1037
- const inspection = await validateSwitchedAccount(appServer);
1053
+ const inspection = await validateSwitchedAccountWithTimeout(appServer);
1038
1054
  const activatedReplacement = {
1039
1055
  ...replacement,
1040
1056
  email: inspection.metadata.email ?? replacement.email,
@@ -3141,8 +3157,27 @@ function getErrorMessage4(payload, fallback) {
3141
3157
  }
3142
3158
  return fallback;
3143
3159
  }
3160
+ function normalizeTelegramAllowlist(values) {
3161
+ const rawValues = Array.isArray(values) ? values : [];
3162
+ const allowAllUsers = rawValues.some((value) => typeof value === "string" && value.trim() === "*");
3163
+ const allowedUserIds = Array.from(new Set(rawValues.map((value) => {
3164
+ if (typeof value === "number" && Number.isFinite(value)) {
3165
+ return Math.trunc(value);
3166
+ }
3167
+ if (typeof value === "string" && value.trim().length > 0) {
3168
+ const normalized = value.trim().replace(/^(telegram|tg):/i, "").trim();
3169
+ if (/^-?\d+$/.test(normalized)) {
3170
+ return Number.parseInt(normalized, 10);
3171
+ }
3172
+ }
3173
+ return Number.NaN;
3174
+ }).filter((value) => Number.isFinite(value)))).slice(0, 100);
3175
+ return { allowAllUsers, allowedUserIds };
3176
+ }
3144
3177
  var TelegramThreadBridge = class {
3145
3178
  constructor(appServer, options = {}) {
3179
+ this.allowAllUsers = false;
3180
+ this.allowedUserIds = /* @__PURE__ */ new Set();
3146
3181
  this.threadIdByChatId = /* @__PURE__ */ new Map();
3147
3182
  this.chatIdsByThreadId = /* @__PURE__ */ new Map();
3148
3183
  this.lastForwardedTurnByThreadId = /* @__PURE__ */ new Map();
@@ -3153,6 +3188,9 @@ var TelegramThreadBridge = class {
3153
3188
  this.appServer = appServer;
3154
3189
  this.token = process.env.TELEGRAM_BOT_TOKEN?.trim() ?? "";
3155
3190
  this.defaultCwd = process.env.TELEGRAM_DEFAULT_CWD?.trim() ?? process.cwd();
3191
+ this.configureAllowedUserIds(
3192
+ (process.env.TELEGRAM_ALLOWED_USER_IDS ?? "").split(",").map((value) => value.trim()).filter(Boolean)
3193
+ );
3156
3194
  this.onChatSeen = options.onChatSeen;
3157
3195
  }
3158
3196
  start() {
@@ -3220,9 +3258,16 @@ var TelegramThreadBridge = class {
3220
3258
  active: this.active,
3221
3259
  mappedChats: this.threadIdByChatId.size,
3222
3260
  mappedThreads: this.chatIdsByThreadId.size,
3261
+ allowedUsers: this.allowedUserIds.size,
3262
+ allowAllUsers: this.allowAllUsers,
3223
3263
  lastError: this.lastError
3224
3264
  };
3225
3265
  }
3266
+ configureAllowedUserIds(allowedUserIds) {
3267
+ const normalized = normalizeTelegramAllowlist(allowedUserIds);
3268
+ this.allowAllUsers = normalized.allowAllUsers;
3269
+ this.allowedUserIds = new Set(normalized.allowedUserIds);
3270
+ }
3226
3271
  connectThread(threadId, chatId, token) {
3227
3272
  const normalizedThreadId = threadId.trim();
3228
3273
  if (!normalizedThreadId) {
@@ -3276,8 +3321,13 @@ var TelegramThreadBridge = class {
3276
3321
  }
3277
3322
  const message = update.message;
3278
3323
  const chatId = message?.chat?.id;
3324
+ const senderId = message?.from?.id;
3279
3325
  const text = message?.text?.trim();
3280
3326
  if (typeof chatId !== "number" || !text) return;
3327
+ if (!this.isAllowedSender(senderId)) {
3328
+ await this.sendTelegramMessage(chatId, this.unauthorizedMessage(senderId));
3329
+ return;
3330
+ }
3281
3331
  this.markChatSeen(chatId);
3282
3332
  if (text === "/start") {
3283
3333
  await this.sendThreadPicker(chatId);
@@ -3310,6 +3360,16 @@ var TelegramThreadBridge = class {
3310
3360
  const callbackId = typeof callbackQuery.id === "string" ? callbackQuery.id : "";
3311
3361
  const data = typeof callbackQuery.data === "string" ? callbackQuery.data : "";
3312
3362
  const chatId = callbackQuery.message?.chat?.id;
3363
+ const senderId = callbackQuery.from?.id;
3364
+ if (!this.isAllowedSender(senderId)) {
3365
+ if (callbackId) {
3366
+ await this.answerCallbackQuery(callbackId, this.unauthorizedCallbackMessage(senderId));
3367
+ }
3368
+ if (typeof chatId === "number") {
3369
+ await this.sendTelegramMessage(chatId, this.unauthorizedMessage(senderId));
3370
+ }
3371
+ return;
3372
+ }
3313
3373
  if (typeof chatId === "number") {
3314
3374
  this.markChatSeen(chatId);
3315
3375
  }
@@ -3331,6 +3391,25 @@ var TelegramThreadBridge = class {
3331
3391
  await this.sendTelegramMessage(chatId, history);
3332
3392
  }
3333
3393
  }
3394
+ isAllowedSender(senderId) {
3395
+ if (this.allowAllUsers) {
3396
+ return typeof senderId === "number" && Number.isFinite(senderId);
3397
+ }
3398
+ return typeof senderId === "number" && Number.isFinite(senderId) && this.allowedUserIds.has(Math.trunc(senderId));
3399
+ }
3400
+ unauthorizedMessage(senderId) {
3401
+ const normalizedSenderId = typeof senderId === "number" && Number.isFinite(senderId) ? String(Math.trunc(senderId)) : "unknown";
3402
+ return `Unauthorized sender.
3403
+
3404
+ Your Telegram user ID: ${normalizedSenderId}
3405
+ Add this ID to the bot allowlist before using the bridge.`;
3406
+ }
3407
+ unauthorizedCallbackMessage(senderId) {
3408
+ if (typeof senderId === "number" && Number.isFinite(senderId)) {
3409
+ return `Unauthorized: ${String(Math.trunc(senderId))}`;
3410
+ }
3411
+ return "Unauthorized sender";
3412
+ }
3334
3413
  async answerCallbackQuery(callbackQueryId, text) {
3335
3414
  await fetch(this.apiUrl("answerCallbackQuery"), {
3336
3415
  method: "POST",
@@ -3499,6 +3578,163 @@ ${summary}`;
3499
3578
  }
3500
3579
  };
3501
3580
 
3581
+ // src/server/freeMode.ts
3582
+ var ENCRYPTED_KEYS = [
3583
+ "FhkYWwEZE0MYBhAGUEADDBYFBEoDBxIHVUpUVRIMVUYDAkEHVRYNAxABUUAEAUMDV0pUDEQAU0ZTDEQCVERQVkoBVhAEBBBXAQ==",
3584
+ "FhkYWwEZE0MYVkIGUkNUUkpVXUsBBUUAVEYHUEIMBhQCVxZWBBcHVkoNUENRAxAMA0MHARBXBkVUAUVQXBAFAUVXVxFWBUtWBw==",
3585
+ "FhkYWwEZE0MYVhFWAUJUUBEMURMHAUoEAUcABRAEURRXARBRU0ZUBBFVB0YAAUNVVUYBDBBSABRUAhdVXUUBUhANV0IMBBABBA==",
3586
+ "FhkYWwEZE0MYUBIDABMBVkMHURcGDRVSXRMFBUUCBBQBVhUBUBEGDBAAVkpTVUoGUBYMBhJXB0ANABUEARBQB0RRXUBRBUNQBw==",
3587
+ "FhkYWwEZE0MYVhcFBhMFARJXVBMADRdWVxYHBkQNA0cBBEsGVEBRAkYCXEFXURBXABZQDEZSXBQHAkJSAUABVxIMA0NWAEcMUA==",
3588
+ "FhkYWwEZE0MYAEQAXEZQURUGARFRBkRWVENWDUYFBkQHBEJWU0dQUkdWBhZXARYMAERXURcBUUQNDUAGAEoEB0ABV0NTUkQDBw==",
3589
+ "FhkYWwEZE0MYABdRURBXVkYDURZWUUIAUEcMBEcMUUpXVxEGVhEMBktVXBBWAEFQBBBRVhECUEQEBkFQUUpRVRUMV0dRA0QGUw==",
3590
+ "FhkYWwEZE0MYVUMCUUIHAUFRVksFVhYMARRRBBIAVBMBDEFVAxMAAUpRXUsEBkMHVhAHBUdSVxECABJSVRdRBkMEURANAEdXVA==",
3591
+ "FhkYWwEZE0MYUkEFVEVUBEoMVkINDUFQUENQAkBSAEVQVhcEAEsDBxVWUkBWAkRSA0ANV0FVBkYMVxcBXUQEUEQCARYDDEpWXA==",
3592
+ "FhkYWwEZE0MYDBcCXBANBRdVU0YEAEsMAUoNUkoMUBYDABcHVEBQBkRVUxdWBUUAAURUVxUGB0oDVxIHABRUAhJRAEUDUkECXQ==",
3593
+ "FhkYWwEZE0MYVhUHVBEBURFQA0EBVRcNUUZXAhVXVkpQAEUHBhNTDUQMXRAGA0JQA0BRBRUHVRQMURVWXUoBVxcEBEYEAEACUQ==",
3594
+ "FhkYWwEZE0MYA0UCB0ZTABUNBhRTAUYAUENQUEANBEMCARdQBkRRBRYDUhdWVhACXBcGBhIDVBFRBRJQUREGDRAAUhQHUhUBAA==",
3595
+ "FhkYWwEZE0MYAUsMARACBBYGVxQHARVQVxRRBRcNUkAFUUQCXBEAUkcBXEMBBEYCXRENBUsBVRYABUJXU0FUAEYBUUpWABEGUw==",
3596
+ "FhkYWwEZE0MYAEdSUBcNAUoFUERUAkcFAEQAB0IBUxcFDUIMBEpQVktWUxdUB0MBXUEBBhIMXRQEUBFQXEMFAEBQXUEMVkoBUQ==",
3597
+ "FhkYWwEZE0MYDBYNUUVUA0ECUREMVhUEBkoCVxJSXEtRUENQVxAMBBcMAxACVkVVBEEDUEYDAURWA0oDXUYAAEEEVxQABUANVg==",
3598
+ "FhkYWwEZE0MYVhcHBhcFV0EBUEdWVxdSU0sMABFSUEIHA0EAU0dWBkcFXEZRBENXAUEDV0AFUkEABBAEXBdXAhZRBEAAUUBSVg==",
3599
+ "FhkYWwEZE0MYV0MHAxYBDUsHB0oFVRJWAUoABEQGAUEHDRdRUBQHBREFUkpQVUYHVkVTDBcDXUYHBkJXUhENVUIAVUtTDRcMVA==",
3600
+ "FhkYWwEZE0MYBxVXBEsAUkYBA0EEAUIGBkRQA0MNABZRVkcHA0JTUEdWUkNRBBUEXUFUB0oFB0QGDBdWUUsEV0NXBBQBVRUEVg==",
3601
+ "FhkYWwEZE0MYVxcCVBYHAEsNVBEADEENUkMAUhFWXBNUVxdWBxNWURYAVEEAUUdSURAHDUsAUkEABBJSVRZQV0YNVRBRDUVSBw==",
3602
+ "FhkYWwEZE0MYAEQEV0YGVxcMVkRQUEsGUEBRUEADAEYEV0NRBBRWBUpXV0oMA0NRXBBRAkAMXUZWURBSXEZRB0EEVBQNVxYBXQ==",
3603
+ "FhkYWwEZE0MYA0IBUhADUERXB0MNB0MHVUtUDRUNBEpXVkEGUBMABkZVUENUAkBVXRAEDEAHBkJWABAEURNWDEcEABQCVUdVVA==",
3604
+ "FhkYWwEZE0MYUhcEBBYCAUFSXUAEAEEBV0sDDRAFV0YDV0NVBENXB0QNVxdUAkYMXUQGA0tRV0tWVUoDAxcGUUVVVUQHDBEDBA==",
3605
+ "FhkYWwEZE0MYA0QBBEIAB0INXUtTBxYFVBFXABJSBkYEAxIAVEAGVkZSB0tQUEoHBkcGBBUHVENWAUNRUEYCAhJSXRcNUEQGUA==",
3606
+ "FhkYWwEZE0MYUBJQUEIFUkBXARBUBUIDAxBQVhJVVkUMVhEDAEYNVxEGBhRUVkJWURYDAUAAAUUMARVRBhYAUkAEUkUEAhYCVg==",
3607
+ "FhkYWwEZE0MYVUYGVBcEDEAFVhACVkFWXBFTAxAMUUpQURENARYBVhABAEINAEQCAUECDEsCBEUBVxBXBxRUVxdQBBRXVkcCUA==",
3608
+ "FhkYWwEZE0MYV0YMVxBTVUNQBERRURVSUxQGUUVWB0MGVUMHV0cHUkMEAEMDDUMCVEcGVkNVB0oDURdVAUFQBkIAURYDBhAHAA==",
3609
+ "FhkYWwEZE0MYUhYCXEsNVUAEURFXBkcHABFQBxcFUENWVkpVAxZWAhEBAxMBBkRRVUtQDEZWXEoBBxUNVkYBBhJQUBBQUUoDBw==",
3610
+ "FhkYWwEZE0MYB0YGXUIGDRIHVRZQVUJSV0UNB0oNAxQADRUMVUcEBhdQV0AHDUZXAEtQVkVQUhBRBUBSUEQCVkRSBEUDUkQEVQ==",
3611
+ "FhkYWwEZE0MYB0MGAUEADERVV0RXDUJSUUMAUEdRAUMCBxZSBktQDUMEBkIBBkIDAEMNURdRXUoFB0RWVEZRDUFXAEJTVRVWVg==",
3612
+ "FhkYWwEZE0MYAkQDAxcFBEpXVEIEDUJSB0tXBhVXUkIDAEEMBBBXDUdRVRcNBURVAxZXUUENARYNBkMAUBBTDUsGVUFWDEcAVQ==",
3613
+ "FhkYWwEZE0MYDRFQVUsCBRIMXBEFBEYEBEMBAUAMVkoCDUoABEcNV0YMU0pWABJXURcAAEIEUBcGVxcGUhZWBUIAVUQCUkVVAA==",
3614
+ "FhkYWwEZE0MYUhcDVURUDBBSVkVWAEYCBkoMUEVVU0cEAxcFVhcDVUEBAEpXUhIAXEUNBUQBAxYGUhFXVUUHBBUAVkpRURAAVQ==",
3615
+ "FhkYWwEZE0MYUEtVV0BQV0tXB0dXVkNRVRRQBUADABFTURYDVBdXUEFSXEpXB0dQVBQEA0oAVUpTBEYEVBMNDEMDU0BUBUINUg==",
3616
+ "FhkYWwEZE0MYUEpQBxZTBkFXXEZWAkEEBxEHB0RXVktQUEMDARRRABEFAUIABEAMARRRDUACBBAMUEMMUxcEUUEEAEoAURBQBg==",
3617
+ "FhkYWwEZE0MYUEINARRQAhcCUEJRUBIFBEIHBkJQB0tWVhINXRBUUBdVBkcMBUZVUxNTAUsMU0cFAEtSXUJXDRJSBkVUAUoBVA==",
3618
+ "FhkYWwEZE0MYUEBWAUtXBxEHUhNTBRZVB0UEB0MDA0sCBEAABkNUAUQCBxYAAEVQUEYDVkoDVxAFVUFVVUcMAUECBkoGUUsCUA==",
3619
+ "FhkYWwEZE0MYBBUBUEFTBUIDAxAGBUcFARBWBRJXUUJTAkUGURQBUUUEB0EFBBBVVURQBEEAUBMAVhINVUEBVUcMA0sNVUNQVg==",
3620
+ "FhkYWwEZE0MYDUMDA0MNA0oAV0BQUEEFBEUBAkpQXBAMBUsCVRZXVkIAXBMEAktXBhYABkcAUhMAUhYBA0BTAURVURMBA0ACXA==",
3621
+ "FhkYWwEZE0MYUhdXUUMDAEQNBhZTAEdWXUYHBxEHXUQCDBYCBkEBBEIDAEBQBkEAUkUBBREMUEUNAEUMBENUBhZXVkVQV0NWBw==",
3622
+ "FhkYWwEZE0MYUBdVU0VXVkpQUkEEBkFSVhZRDEQMXBdQA0QDXUIHUEtWBEABAERXBEoMAxEDUxYMDUVRXUJTUBcFBhQCVkcAVQ==",
3623
+ "FhkYWwEZE0MYDRcBUBYCUEpRABQNVRcHBkQABkZXUkBWURFVU0sMBxINURQMAREHUUcHAUYDB0QBAUNVVUAAARUGUEoAABYGXA==",
3624
+ "FhkYWwEZE0MYAhUCUktQV0oEBxcCVUoCB0pTAUJQXUJTARYAXUsNDRUDUkVQARIHUUJUBEMEVxMFBhEAUUUCBRIAXBMBVUAHAA==",
3625
+ "FhkYWwEZE0MYARcCAUMDVxFQBEZXUEcCAEYDVksNVUpQB0MEB0EEAhVWXRcEUUBQXEoGA0BSAEVWBhdXVUACUUZSUEZXBUZRVg==",
3626
+ "FhkYWwEZE0MYA0oBUhEHABYCBkcMUUcGXRdUDRZXVBZTARUHVkQGV0UAAUoDBxJRVRQBA0oFAUEGBERSVhMGBEBWBEpRAUMGVg==",
3627
+ "FhkYWwEZE0MYVhANVBFXDEoAXEoMBhYMUxMNUEECVEoMAkQFVxANVRcFABdRDRVWXUACBBYHVURUUBACXUNUV0IMAUcCAUIHBw==",
3628
+ "FhkYWwEZE0MYUEUCB0UNDBEFVhQGBhJSBkYHBEMNBhAGUkAFA0AMBUAGUEYDAUZQVUdWUhcHUEcAUhEFB0FQV0VQAUtXBEcDBw==",
3629
+ "FhkYWwEZE0MYAUNSURNTBRANUkRWVxYHXEYMAUYBB0cCBksCAEUHUkVSVUdRURIEBxADBhECVxNRUEcFUxQMUEMMA0MFBEENBA==",
3630
+ "FhkYWwEZE0MYBxBWVkZQAkENVhYNBBUDXBADAUANUkBRVkICBEtRVxEEAEFUDUIDUkNXAxEDAUcHARIAVEJTA0oNVkANB0EGXA==",
3631
+ "FhkYWwEZE0MYV0IGAREGUhZVUEFXVkJVVUECDBcFARcNUhZQXBEFAkMBU0sDUEIFBhcCV0cCXUBTVRADV0YEA0ZQUxACUUsEXA==",
3632
+ "FhkYWwEZE0MYVUoNVUNQURVQUREDAxJSB0AAAkMCABQDAUMGVBYABBJWBxADBENRVEsAVxcAAEdTARUDBBEFUhZSUEQDDRcAXA==",
3633
+ "FhkYWwEZE0MYBhdRVUQBVhcGBhENV0QEAEIGVxcBUxQNUUcAVxZWBEEEVUBTARYGAUFUAUUHBEQEDUsDVEJXDEQNU0sCV0YMAA==",
3634
+ "FhkYWwEZE0MYVktRAENWAkEBXUsGUUEDUkMHB0FXB0cCAhEHARZUBxJWB0IDBERRUkUAAxYDUkYBUkYDV0QDVUcHVxAGA0INVA==",
3635
+ "FhkYWwEZE0MYBkIAVBABVUINV0NTVxYGAUYFVUMBUxYMURAGBksAVUJVBkoHUUQMA0cMAUdQVkUGBUpSAEZTVRANUBYEUEQBVA==",
3636
+ "FhkYWwEZE0MYDEMDA0oCVURRUBZWUkANUEQBAkBSXBBRB0BSVBYGVkAFVRAGDEYDVkMEDBBSBkQHV0JRA0JUBRcFXURTDRZRUw==",
3637
+ "FhkYWwEZE0MYUBJSVEUNUhcGBkYGAhYDUEcAUEsEBxdUUBZSXEcGDEINXEoDBxcFUkdWBEBRVxYNDUYAUBMBVkcCUBcHUUsAXA==",
3638
+ "FhkYWwEZE0MYA0QNUEtWAUMEAEoBUEoAAREDVRYHVkoEV0ZQBEEEUkEAAUIEAUcCVhEHUBUDB0FRVktVVEFXARIHA0NWBUpXBg==",
3639
+ "FhkYWwEZE0MYV0oCAxFXV0YMUxFQAUAAUEVWA0cEB0pUUBAFXUIHDRdQAEQAUkMGU0AEVxEDBBEBAhBRV0QEAxVWU0NQBxECBw==",
3640
+ "FhkYWwEZE0MYUkoCBBMEUkYDBhYFB0IFVkQMVUpSBksMUUcFUxEEDRZVVkUADEoCVhcHUkEHUxACUUJXBBdTVUQDAEFRBxUGAw==",
3641
+ "FhkYWwEZE0MYV0BXBEUCURUNXUYABUpQURYBBUYFVENXBhYCXRBWDUsHA0NRB0oGUREHDRYGUxMBAksEUkINVkNVBEIBAxdWUA==",
3642
+ "FhkYWwEZE0MYBEoCA0MNDEUAXUNUAxYHXBRRURACUEFQBxYDB0dUBksDA0QFVksMAxQFAEZRAxNTA0cMVUENVxEEAUFRBUAHBA==",
3643
+ "FhkYWwEZE0MYB0ENVkAABxEFXEZQAUYHBhMFBBJQBkMCUUZRBERTAkBRBhADVxYEXEADB0RSUUtRUREBXRdXBkAGBEUNBEQCUg==",
3644
+ "FhkYWwEZE0MYVhFVXBAFDBYFBkFTAhIGB0ZRBEJVXURTBBFWBxNUVkBXBEMMBkIHURcDAksAVEMEV0dSUhdXBBJWUkJWAxBRVg==",
3645
+ "FhkYWwEZE0MYBRFRXUoBBUcGBhZUAUBVABYEVkICVkNXDUcMABYFVxdXU0sEBkcFXEYBURUFVUYFABJSA0dTABdRVBRQVRENVg==",
3646
+ "FhkYWwEZE0MYV0VQVhEHVkVVAEFQAhcHBEMMUUANVxNUBUJWVRcEVUoEBBcDUkcNBkoCUUVVVBcDVRZQBBBUVRYNXBZWBUMAAQ==",
3647
+ "FhkYWwEZE0MYAhIHARYBVxIABhYBVxVWUkVXV0ICXUUADBZQAUYBBEUBUEsFBRcFARcGUEtVXEoGABYAUkEGDEBRVUMBB0IFXQ==",
3648
+ "FhkYWwEZE0MYBBUCXEEFBEJVVxZQBEICUxFRBUUNBksHBhFXUUANVhUCV0EMB0QDVURWDBFSVkYDAxYAABdXBhIEUEQMBEUCUg==",
3649
+ "FhkYWwEZE0MYDUsDBEoNAEECURBTAUJXUEpWVUdSBEAMDRBVUkYAUUoDAxFTDEpVVxEHAUYNVUMGVkYAA0cBBRJWBBAHUUIMVw==",
3650
+ "FhkYWwEZE0MYUUUFXUBTUEYHUkoHBBIFBEpRV0NWVRcBAktWBkADBxAMVUZUBhJSBEAEB0MDVkpTABUMUUVUUEABUkJUVkIHAA=="
3651
+ ];
3652
+ var DECRYPT_KEY = "er54s4";
3653
+ function xorDecrypt(b64, secret) {
3654
+ const buf = Buffer.from(b64, "base64");
3655
+ const keyBuf = Buffer.from(secret, "utf8");
3656
+ const out = Buffer.alloc(buf.length);
3657
+ for (let i = 0; i < buf.length; i++) {
3658
+ out[i] = buf[i] ^ keyBuf[i % keyBuf.length];
3659
+ }
3660
+ return out.toString("utf8");
3661
+ }
3662
+ function getRandomFreeKey() {
3663
+ if (ENCRYPTED_KEYS.length === 0) return null;
3664
+ const idx = Math.floor(Math.random() * ENCRYPTED_KEYS.length);
3665
+ return xorDecrypt(ENCRYPTED_KEYS[idx], DECRYPT_KEY);
3666
+ }
3667
+ function getFreeKeyCount() {
3668
+ return ENCRYPTED_KEYS.length;
3669
+ }
3670
+ var FREE_MODE_PROVIDER_ID = "openrouter-free";
3671
+ var FREE_MODE_BASE_URL = "https://openrouter.ai/api/v1";
3672
+ var FALLBACK_FREE_MODELS = [
3673
+ "openrouter/free",
3674
+ "google/gemma-4-26b-a4b-it:free",
3675
+ "google/gemma-3-27b-it:free",
3676
+ "meta-llama/llama-3.3-70b-instruct:free",
3677
+ "qwen/qwen3-coder:free"
3678
+ ];
3679
+ var cachedFreeModels = null;
3680
+ var cacheTimestamp = 0;
3681
+ var CACHE_TTL_MS = 10 * 60 * 1e3;
3682
+ async function fetchFreeModelsFromOpenRouter() {
3683
+ try {
3684
+ const resp = await fetch("https://openrouter.ai/api/v1/models");
3685
+ if (!resp.ok) return cachedFreeModels ?? FALLBACK_FREE_MODELS;
3686
+ const json = await resp.json();
3687
+ const ids = json.data.filter((m) => m.id.endsWith(":free") || m.id === "openrouter/free").map((m) => m.id);
3688
+ if (ids.length === 0) return cachedFreeModels ?? FALLBACK_FREE_MODELS;
3689
+ const sorted = ["openrouter/free", ...ids.filter((id) => id !== "openrouter/free")];
3690
+ cachedFreeModels = sorted;
3691
+ cacheTimestamp = Date.now();
3692
+ return sorted;
3693
+ } catch {
3694
+ return cachedFreeModels ?? FALLBACK_FREE_MODELS;
3695
+ }
3696
+ }
3697
+ async function getFreeModels() {
3698
+ if (cachedFreeModels && Date.now() - cacheTimestamp < CACHE_TTL_MS) {
3699
+ return cachedFreeModels;
3700
+ }
3701
+ return fetchFreeModelsFromOpenRouter();
3702
+ }
3703
+ var FREE_MODE_DEFAULT_MODEL = "openrouter/free";
3704
+ var FREE_MODE_STATE_FILE = "webui-free-mode.json";
3705
+ var CUSTOM_PROVIDER_ID = "custom-endpoint";
3706
+ function getFreeModeConfigArgs(state) {
3707
+ if (!state.enabled || !state.apiKey) return [];
3708
+ if (state.provider === "custom" && state.customBaseUrl) {
3709
+ return [
3710
+ "-c",
3711
+ `model_provider="${CUSTOM_PROVIDER_ID}"`,
3712
+ "-c",
3713
+ `model_providers.${CUSTOM_PROVIDER_ID}.name="Custom Endpoint"`,
3714
+ "-c",
3715
+ `model_providers.${CUSTOM_PROVIDER_ID}.base_url="${state.customBaseUrl}"`,
3716
+ "-c",
3717
+ `model_providers.${CUSTOM_PROVIDER_ID}.wire_api="responses"`,
3718
+ "-c",
3719
+ `model_providers.${CUSTOM_PROVIDER_ID}.experimental_bearer_token="${state.apiKey}"`
3720
+ ];
3721
+ }
3722
+ return [
3723
+ "-c",
3724
+ `model="${state.model}"`,
3725
+ "-c",
3726
+ `model_provider="${FREE_MODE_PROVIDER_ID}"`,
3727
+ "-c",
3728
+ `model_providers.${FREE_MODE_PROVIDER_ID}.name="OpenRouter Free"`,
3729
+ "-c",
3730
+ `model_providers.${FREE_MODE_PROVIDER_ID}.base_url="${FREE_MODE_BASE_URL}"`,
3731
+ "-c",
3732
+ `model_providers.${FREE_MODE_PROVIDER_ID}.wire_api="responses"`,
3733
+ "-c",
3734
+ `model_providers.${FREE_MODE_PROVIDER_ID}.experimental_bearer_token="${state.apiKey}"`
3735
+ ];
3736
+ }
3737
+
3502
3738
  // src/utils/commandInvocation.ts
3503
3739
  import { spawnSync as spawnSync2 } from "child_process";
3504
3740
  import { basename as basename2, extname } from "path";
@@ -4979,11 +5215,24 @@ async function writeWorkspaceRootsState(nextState) {
4979
5215
  }
4980
5216
  function normalizeTelegramBridgeConfig(value) {
4981
5217
  const record = asRecord5(value);
4982
- if (!record) return { botToken: "", chatIds: [] };
5218
+ if (!record) return { botToken: "", chatIds: [], allowedUserIds: [] };
4983
5219
  const botToken = typeof record.botToken === "string" ? record.botToken.trim() : "";
4984
5220
  const rawChatIds = Array.isArray(record.chatIds) ? record.chatIds : [];
4985
5221
  const chatIds = Array.from(new Set(rawChatIds.filter((value2) => typeof value2 === "number" && Number.isFinite(value2)).map((value2) => Math.trunc(value2)))).slice(0, 50);
4986
- return { botToken, chatIds };
5222
+ const rawAllowedUserIds = Array.isArray(record.allowedUserIds) ? record.allowedUserIds : [];
5223
+ const allowAllUsers = rawAllowedUserIds.some((value2) => typeof value2 === "string" && value2.trim() === "*");
5224
+ const normalizedAllowedUserIds = Array.from(new Set(rawAllowedUserIds.map((value2) => {
5225
+ if (typeof value2 === "number" && Number.isFinite(value2)) return Math.trunc(value2);
5226
+ if (typeof value2 === "string") {
5227
+ const normalized = value2.trim().replace(/^(telegram|tg):/i, "").trim();
5228
+ if (/^-?\d+$/.test(normalized)) {
5229
+ return Number.parseInt(normalized, 10);
5230
+ }
5231
+ }
5232
+ return Number.NaN;
5233
+ }).filter((value2) => Number.isFinite(value2)))).slice(0, 100);
5234
+ const allowedUserIds = allowAllUsers ? ["*", ...normalizedAllowedUserIds] : normalizedAllowedUserIds;
5235
+ return { botToken, chatIds, allowedUserIds };
4987
5236
  }
4988
5237
  async function readTelegramBridgeConfig() {
4989
5238
  const telegramConfigPath = getTelegramBridgeConfigPath();
@@ -4992,7 +5241,7 @@ async function readTelegramBridgeConfig() {
4992
5241
  const payload = asRecord5(JSON.parse(raw)) ?? {};
4993
5242
  return normalizeTelegramBridgeConfig(payload);
4994
5243
  } catch {
4995
- return { botToken: "", chatIds: [] };
5244
+ return { botToken: "", chatIds: [], allowedUserIds: [] };
4996
5245
  }
4997
5246
  }
4998
5247
  async function writeTelegramBridgeConfig(nextState) {
@@ -5000,7 +5249,8 @@ async function writeTelegramBridgeConfig(nextState) {
5000
5249
  const telegramConfigPath = getTelegramBridgeConfigPath();
5001
5250
  await writeFile4(telegramConfigPath, JSON.stringify({
5002
5251
  botToken: normalized.botToken,
5003
- chatIds: normalized.chatIds
5252
+ chatIds: normalized.chatIds,
5253
+ allowedUserIds: normalized.allowedUserIds
5004
5254
  }), "utf8");
5005
5255
  }
5006
5256
  var telegramBridgeConfigMutation = Promise.resolve();
@@ -5206,10 +5456,27 @@ var AppServerProcess = class {
5206
5456
  }
5207
5457
  return codexCommand;
5208
5458
  }
5459
+ buildAppServerArgs() {
5460
+ const args = [
5461
+ "app-server",
5462
+ "-c",
5463
+ 'approval_policy="never"',
5464
+ "-c",
5465
+ 'sandbox_mode="danger-full-access"'
5466
+ ];
5467
+ const statePath = join5(getCodexHomeDir3(), FREE_MODE_STATE_FILE);
5468
+ try {
5469
+ const raw = readFileSync(statePath, "utf8");
5470
+ const state = JSON.parse(raw);
5471
+ args.push(...getFreeModeConfigArgs(state));
5472
+ } catch {
5473
+ }
5474
+ return args;
5475
+ }
5209
5476
  start() {
5210
5477
  if (this.process) return;
5211
5478
  this.stopping = false;
5212
- const invocation = getSpawnInvocation(this.getCodexCommand(), this.appServerArgs);
5479
+ const invocation = getSpawnInvocation(this.getCodexCommand(), this.buildAppServerArgs());
5213
5480
  const proc = spawn4(invocation.command, invocation.args, { stdio: ["pipe", "pipe", "pipe"] });
5214
5481
  this.process = proc;
5215
5482
  proc.stdout.setEncoding("utf8");
@@ -5758,6 +6025,7 @@ function createCodexBridgeMiddleware() {
5758
6025
  void readTelegramBridgeConfig().then((config) => {
5759
6026
  if (!config.botToken) return;
5760
6027
  telegramBridge.configureToken(config.botToken);
6028
+ telegramBridge.configureAllowedUserIds(config.allowedUserIds);
5761
6029
  telegramBridge.start();
5762
6030
  }).catch(() => {
5763
6031
  });
@@ -5768,6 +6036,135 @@ function createCodexBridgeMiddleware() {
5768
6036
  return;
5769
6037
  }
5770
6038
  const url = new URL(req.url, "http://localhost");
6039
+ if (url.pathname.startsWith("/codex-api/free-mode")) {
6040
+ let readFreeModeState2 = function() {
6041
+ try {
6042
+ return JSON.parse(readFileSync(statePath, "utf8"));
6043
+ } catch {
6044
+ return { enabled: false, apiKey: null, model: FREE_MODE_DEFAULT_MODEL };
6045
+ }
6046
+ };
6047
+ var readFreeModeState = readFreeModeState2;
6048
+ const statePath = join5(getCodexHomeDir3(), FREE_MODE_STATE_FILE);
6049
+ if (req.method === "POST" && url.pathname === "/codex-api/free-mode") {
6050
+ try {
6051
+ const body = await readJsonBody(req);
6052
+ const enable = Boolean(body?.enable);
6053
+ if (enable) {
6054
+ const apiKey = getRandomFreeKey();
6055
+ if (!apiKey) {
6056
+ setJson4(res, 500, { error: "No free keys available" });
6057
+ return;
6058
+ }
6059
+ const state = { enabled: true, apiKey, model: FREE_MODE_DEFAULT_MODEL, provider: "openrouter" };
6060
+ await writeFile4(statePath, JSON.stringify(state), "utf8");
6061
+ appServer.dispose();
6062
+ const freeModels = await getFreeModels();
6063
+ setJson4(res, 200, {
6064
+ ok: true,
6065
+ enabled: true,
6066
+ model: FREE_MODE_DEFAULT_MODEL,
6067
+ keyCount: getFreeKeyCount(),
6068
+ models: freeModels
6069
+ });
6070
+ } else {
6071
+ const state = { enabled: false, apiKey: null, model: FREE_MODE_DEFAULT_MODEL };
6072
+ await writeFile4(statePath, JSON.stringify(state), "utf8");
6073
+ appServer.dispose();
6074
+ setJson4(res, 200, { ok: true, enabled: false });
6075
+ }
6076
+ } catch (error) {
6077
+ setJson4(res, 500, { error: getErrorMessage5(error, "Failed to toggle free mode") });
6078
+ }
6079
+ return;
6080
+ }
6081
+ if (req.method === "GET" && url.pathname === "/codex-api/free-mode/status") {
6082
+ try {
6083
+ const state = readFreeModeState2();
6084
+ const freeModels = await getFreeModels();
6085
+ const maskedKey = state.apiKey && state.customKey ? state.apiKey.substring(0, 12) + "..." + state.apiKey.substring(state.apiKey.length - 4) : null;
6086
+ setJson4(res, 200, {
6087
+ enabled: state.enabled,
6088
+ keyCount: getFreeKeyCount(),
6089
+ models: freeModels,
6090
+ currentModel: state.enabled ? state.model : null,
6091
+ customKey: Boolean(state.customKey),
6092
+ maskedKey,
6093
+ provider: state.provider ?? "openrouter",
6094
+ customBaseUrl: state.customBaseUrl ?? null
6095
+ });
6096
+ } catch (error) {
6097
+ setJson4(res, 500, { error: getErrorMessage5(error, "Failed to read free mode status") });
6098
+ }
6099
+ return;
6100
+ }
6101
+ if (req.method === "POST" && url.pathname === "/codex-api/free-mode/rotate-key") {
6102
+ try {
6103
+ const apiKey = getRandomFreeKey();
6104
+ if (!apiKey) {
6105
+ setJson4(res, 500, { error: "No free keys available" });
6106
+ return;
6107
+ }
6108
+ const current = readFreeModeState2();
6109
+ const state = { ...current, apiKey, customKey: false };
6110
+ await writeFile4(statePath, JSON.stringify(state), "utf8");
6111
+ appServer.dispose();
6112
+ setJson4(res, 200, { ok: true });
6113
+ } catch (error) {
6114
+ setJson4(res, 500, { error: getErrorMessage5(error, "Failed to rotate key") });
6115
+ }
6116
+ return;
6117
+ }
6118
+ if (req.method === "POST" && url.pathname === "/codex-api/free-mode/custom-key") {
6119
+ try {
6120
+ const body = await readJsonBody(req);
6121
+ const key = typeof body?.key === "string" ? body.key.trim() : "";
6122
+ const current = readFreeModeState2();
6123
+ if (key.length > 0) {
6124
+ const state = { ...current, enabled: true, apiKey: key, customKey: true, provider: "openrouter" };
6125
+ await writeFile4(statePath, JSON.stringify(state), "utf8");
6126
+ appServer.dispose();
6127
+ setJson4(res, 200, { ok: true, customKey: true });
6128
+ } else {
6129
+ const communityKey = getRandomFreeKey();
6130
+ const state = { ...current, apiKey: communityKey, customKey: false, provider: "openrouter" };
6131
+ await writeFile4(statePath, JSON.stringify(state), "utf8");
6132
+ appServer.dispose();
6133
+ setJson4(res, 200, { ok: true, customKey: false });
6134
+ }
6135
+ } catch (error) {
6136
+ setJson4(res, 500, { error: getErrorMessage5(error, "Failed to set custom key") });
6137
+ }
6138
+ return;
6139
+ }
6140
+ if (req.method === "POST" && url.pathname === "/codex-api/free-mode/custom-provider") {
6141
+ try {
6142
+ const body = await readJsonBody(req);
6143
+ const baseUrl = typeof body?.baseUrl === "string" ? body.baseUrl.trim() : "";
6144
+ const apiKey = typeof body?.apiKey === "string" ? body.apiKey.trim() : "";
6145
+ if (!baseUrl) {
6146
+ setJson4(res, 400, { error: "baseUrl is required" });
6147
+ return;
6148
+ }
6149
+ const state = {
6150
+ enabled: true,
6151
+ apiKey: apiKey || "dummy",
6152
+ model: "",
6153
+ customKey: true,
6154
+ provider: "custom",
6155
+ customBaseUrl: baseUrl
6156
+ };
6157
+ await writeFile4(statePath, JSON.stringify(state), "utf8");
6158
+ appServer.dispose();
6159
+ setJson4(res, 200, { ok: true });
6160
+ } catch (error) {
6161
+ setJson4(res, 500, { error: getErrorMessage5(error, "Failed to set custom provider") });
6162
+ }
6163
+ return;
6164
+ }
6165
+ next();
6166
+ return;
6167
+ }
5771
6168
  if (!url.pathname.startsWith("/codex-api/")) {
5772
6169
  next();
5773
6170
  return;
@@ -6038,6 +6435,34 @@ function createCodexBridgeMiddleware() {
6038
6435
  return;
6039
6436
  }
6040
6437
  if (req.method === "GET" && url.pathname === "/codex-api/provider-models") {
6438
+ try {
6439
+ const fmState = JSON.parse(readFileSync(join5(getCodexHomeDir3(), FREE_MODE_STATE_FILE), "utf8"));
6440
+ if (fmState.enabled) {
6441
+ if (fmState.provider === "custom" && fmState.customBaseUrl) {
6442
+ try {
6443
+ const modelsUrl = fmState.customBaseUrl.replace(/\/+$/, "") + "/models";
6444
+ const headers = {};
6445
+ if (fmState.apiKey && fmState.apiKey !== "dummy") {
6446
+ headers["Authorization"] = `Bearer ${fmState.apiKey}`;
6447
+ }
6448
+ const resp = await fetch(modelsUrl, { headers, signal: AbortSignal.timeout(8e3) });
6449
+ if (resp.ok) {
6450
+ const json = await resp.json();
6451
+ const ids = (json.data ?? []).map((m) => m.id).filter(Boolean);
6452
+ setJson4(res, 200, { data: ids, exclusive: true, source: "custom" });
6453
+ return;
6454
+ }
6455
+ } catch {
6456
+ }
6457
+ setJson4(res, 200, { data: [], exclusive: true, source: "custom" });
6458
+ return;
6459
+ }
6460
+ const freeModels = await getFreeModels();
6461
+ setJson4(res, 200, { data: freeModels, exclusive: true });
6462
+ return;
6463
+ }
6464
+ } catch {
6465
+ }
6041
6466
  const data = await readProviderBackedModelIds(appServer);
6042
6467
  setJson4(res, 200, data);
6043
6468
  return;
@@ -6495,20 +6920,41 @@ function createCodexBridgeMiddleware() {
6495
6920
  if (req.method === "POST" && url.pathname === "/codex-api/telegram/configure-bot") {
6496
6921
  const payload = asRecord5(await readJsonBody(req));
6497
6922
  const botToken = typeof payload?.botToken === "string" ? payload.botToken.trim() : "";
6923
+ const rawAllowedUserIds = Array.isArray(payload?.allowedUserIds) ? payload.allowedUserIds : [];
6498
6924
  if (!botToken) {
6499
6925
  setJson4(res, 400, { error: "Missing botToken" });
6500
6926
  return;
6501
6927
  }
6502
- telegramBridge.configureToken(botToken);
6928
+ const config = normalizeTelegramBridgeConfig({
6929
+ botToken,
6930
+ allowedUserIds: rawAllowedUserIds
6931
+ });
6932
+ if (config.allowedUserIds.length === 0) {
6933
+ setJson4(res, 400, { error: "At least one allowed Telegram user ID is required" });
6934
+ return;
6935
+ }
6936
+ telegramBridge.configureToken(config.botToken);
6937
+ telegramBridge.configureAllowedUserIds(config.allowedUserIds);
6503
6938
  telegramBridge.start();
6504
6939
  const existingConfig = await readTelegramBridgeConfig();
6505
6940
  await writeTelegramBridgeConfig({
6506
- botToken,
6507
- chatIds: existingConfig.chatIds
6941
+ botToken: config.botToken,
6942
+ chatIds: existingConfig.chatIds,
6943
+ allowedUserIds: config.allowedUserIds
6508
6944
  });
6509
6945
  setJson4(res, 200, { ok: true });
6510
6946
  return;
6511
6947
  }
6948
+ if (req.method === "GET" && url.pathname === "/codex-api/telegram/config") {
6949
+ const config = await readTelegramBridgeConfig();
6950
+ setJson4(res, 200, {
6951
+ data: {
6952
+ botToken: config.botToken,
6953
+ allowedUserIds: config.allowedUserIds
6954
+ }
6955
+ });
6956
+ return;
6957
+ }
6512
6958
  if (req.method === "GET" && url.pathname === "/codex-api/telegram/status") {
6513
6959
  setJson4(res, 200, { data: telegramBridge.getStatus() });
6514
6960
  return;
@@ -7776,6 +8222,8 @@ async function startServer(options) {
7776
8222
  if (options.login && !hasCodexAuth() && codexCommand) {
7777
8223
  console.log("\nCodex is not logged in. Starting `codex login`...\n");
7778
8224
  runOrFail(codexCommand, ["login"], "Codex login");
8225
+ } else if (options.login && !hasCodexAuth()) {
8226
+ console.log("\nCodex is not logged in. You can log in later via settings or run `codexui login`.\n");
7779
8227
  }
7780
8228
  const requestedPort = parseInt(options.port, 10);
7781
8229
  const password = resolvePassword(options.password);