codexuse-cli 3.9.2 → 3.9.7
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/README.md +8 -4
- package/dist/index.js +1353 -460
- package/dist/index.js.map +1 -1
- package/dist/server/index.mjs +2242 -673
- package/dist/server/{open-DX6_a9Ta.mjs → open-BWXrZXJl.mjs} +6 -6
- package/package.json +1 -1
package/dist/server/index.mjs
CHANGED
|
@@ -8,9 +8,9 @@ import crypto$1, { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
|
8
8
|
import * as NFS from "node:fs";
|
|
9
9
|
import fs, { accessSync, constants, existsSync, promises, readFileSync, realpathSync, statSync } from "node:fs";
|
|
10
10
|
import * as OS from "node:os";
|
|
11
|
-
import
|
|
11
|
+
import nodeOs, { homedir } from "node:os";
|
|
12
12
|
import * as Path from "node:path";
|
|
13
|
-
import
|
|
13
|
+
import nodePath, { extname, join } from "node:path";
|
|
14
14
|
import * as NodeUrl from "node:url";
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
16
|
import * as readline$1 from "node:readline";
|
|
@@ -19,7 +19,7 @@ import * as Net from "node:net";
|
|
|
19
19
|
import fs$1, { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
20
20
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
21
21
|
import { EventEmitter } from "node:events";
|
|
22
|
-
import path
|
|
22
|
+
import path, { join as join$1 } from "path";
|
|
23
23
|
import util from "node:util";
|
|
24
24
|
import http from "node:http";
|
|
25
25
|
import { WebSocketServer } from "ws";
|
|
@@ -47814,7 +47814,8 @@ const UserInputQuestion = Struct({
|
|
|
47814
47814
|
id: TrimmedNonEmptyStringSchema$1,
|
|
47815
47815
|
header: TrimmedNonEmptyStringSchema$1,
|
|
47816
47816
|
question: TrimmedNonEmptyStringSchema$1,
|
|
47817
|
-
options: Array$1(UserInputQuestionOption)
|
|
47817
|
+
options: Array$1(UserInputQuestionOption),
|
|
47818
|
+
multiSelect: optional$2(Boolean$2)
|
|
47818
47819
|
});
|
|
47819
47820
|
const UserInputRequestedPayload = Struct({ questions: Array$1(UserInputQuestion) });
|
|
47820
47821
|
const UserInputResolvedPayload = Struct({ answers: UnknownRecordSchema });
|
|
@@ -48763,6 +48764,13 @@ const WS_METHODS = {
|
|
|
48763
48764
|
autoRollGetSettings: "autoRoll.getSettings",
|
|
48764
48765
|
autoRollSaveSettings: "autoRoll.saveSettings",
|
|
48765
48766
|
officialCodexRestart: "officialCodex.restart",
|
|
48767
|
+
officialCodexInstances: "officialCodex.instances",
|
|
48768
|
+
officialCodexLaunchProfile: "officialCodex.launchProfile",
|
|
48769
|
+
officialCodexFocusProfile: "officialCodex.focusProfile",
|
|
48770
|
+
officialCodexStopProfile: "officialCodex.stopProfile",
|
|
48771
|
+
officialCodexRestartProfile: "officialCodex.restartProfile",
|
|
48772
|
+
officialCodexLaunchProfiles: "officialCodex.launchProfiles",
|
|
48773
|
+
officialCodexStopProfiles: "officialCodex.stopProfiles",
|
|
48766
48774
|
cliInfo: "cli.info",
|
|
48767
48775
|
cliStatus: "cli.status",
|
|
48768
48776
|
cliCheckFreshness: "cli.checkFreshness",
|
|
@@ -48907,7 +48915,11 @@ const WebSocketRequestBody = Union([
|
|
|
48907
48915
|
accountKey: TrimmedNonEmptyString,
|
|
48908
48916
|
groupName: Union([TrimmedNonEmptyString, Null])
|
|
48909
48917
|
})),
|
|
48910
|
-
tagRequestBody(WS_METHODS.profilesDelete, Struct({
|
|
48918
|
+
tagRequestBody(WS_METHODS.profilesDelete, Struct({
|
|
48919
|
+
name: TrimmedNonEmptyString,
|
|
48920
|
+
replacementProfileName: optional$2(Union([TrimmedNonEmptyString, Null])),
|
|
48921
|
+
removeActiveAuth: optional$2(Boolean$2)
|
|
48922
|
+
})),
|
|
48911
48923
|
tagRequestBody(WS_METHODS.profilesExport, Struct({ name: TrimmedNonEmptyString })),
|
|
48912
48924
|
tagRequestBody(WS_METHODS.profilesCancelAuth, Struct({ name: TrimmedNonEmptyString })),
|
|
48913
48925
|
tagRequestBody(WS_METHODS.rateLimitsRefreshNow, Struct({})),
|
|
@@ -48919,6 +48931,13 @@ const WebSocketRequestBody = Union([
|
|
|
48919
48931
|
tagRequestBody(WS_METHODS.autoRollGetSettings, Struct({})),
|
|
48920
48932
|
tagRequestBody(WS_METHODS.autoRollSaveSettings, Struct({ settings: Unknown })),
|
|
48921
48933
|
tagRequestBody(WS_METHODS.officialCodexRestart, Struct({})),
|
|
48934
|
+
tagRequestBody(WS_METHODS.officialCodexInstances, Struct({})),
|
|
48935
|
+
tagRequestBody(WS_METHODS.officialCodexLaunchProfile, Struct({ name: TrimmedNonEmptyString })),
|
|
48936
|
+
tagRequestBody(WS_METHODS.officialCodexFocusProfile, Struct({ name: TrimmedNonEmptyString })),
|
|
48937
|
+
tagRequestBody(WS_METHODS.officialCodexStopProfile, Struct({ name: TrimmedNonEmptyString })),
|
|
48938
|
+
tagRequestBody(WS_METHODS.officialCodexRestartProfile, Struct({ name: TrimmedNonEmptyString })),
|
|
48939
|
+
tagRequestBody(WS_METHODS.officialCodexLaunchProfiles, Struct({ names: Array$1(TrimmedNonEmptyString) })),
|
|
48940
|
+
tagRequestBody(WS_METHODS.officialCodexStopProfiles, Struct({ names: Array$1(TrimmedNonEmptyString) })),
|
|
48922
48941
|
tagRequestBody(WS_METHODS.cliInfo, Struct({})),
|
|
48923
48942
|
tagRequestBody(WS_METHODS.cliStatus, Struct({})),
|
|
48924
48943
|
tagRequestBody(WS_METHODS.cliCheckFreshness, Struct({})),
|
|
@@ -49196,7 +49215,7 @@ const launchDetached = (launch) => gen(function* () {
|
|
|
49196
49215
|
});
|
|
49197
49216
|
const make$9 = gen(function* () {
|
|
49198
49217
|
const open = yield* tryPromise({
|
|
49199
|
-
try: () => import("./open-
|
|
49218
|
+
try: () => import("./open-BWXrZXJl.mjs"),
|
|
49200
49219
|
catch: (cause) => new OpenError({
|
|
49201
49220
|
message: "failed to load browser opener",
|
|
49202
49221
|
cause
|
|
@@ -54296,8 +54315,8 @@ const IGNORED_DIRECTORY_NAMES = new Set([
|
|
|
54296
54315
|
".cache"
|
|
54297
54316
|
]);
|
|
54298
54317
|
function expandHomePath(input) {
|
|
54299
|
-
if (input === "~") return
|
|
54300
|
-
if (input.startsWith("~/") || input.startsWith("~\\")) return
|
|
54318
|
+
if (input === "~") return nodeOs.homedir();
|
|
54319
|
+
if (input.startsWith("~/") || input.startsWith("~\\")) return nodePath.join(nodeOs.homedir(), input.slice(2));
|
|
54301
54320
|
return input;
|
|
54302
54321
|
}
|
|
54303
54322
|
function isWindowsAbsolutePath(input) {
|
|
@@ -54306,13 +54325,13 @@ function isWindowsAbsolutePath(input) {
|
|
|
54306
54325
|
function resolveBrowseTarget(input) {
|
|
54307
54326
|
if (process.platform !== "win32" && isWindowsAbsolutePath(input.partialPath)) throw new Error("Windows-style paths are only supported on Windows.");
|
|
54308
54327
|
const expanded = expandHomePath(input.partialPath);
|
|
54309
|
-
if (
|
|
54310
|
-
return input.cwd ?
|
|
54328
|
+
if (nodePath.isAbsolute(expanded) || input.partialPath.startsWith("~")) return nodePath.resolve(expanded);
|
|
54329
|
+
return input.cwd ? nodePath.resolve(expandHomePath(input.cwd), expanded) : nodePath.resolve(expanded);
|
|
54311
54330
|
}
|
|
54312
54331
|
const workspaceIndexCache = /* @__PURE__ */ new Map();
|
|
54313
54332
|
const inFlightWorkspaceIndexBuilds = /* @__PURE__ */ new Map();
|
|
54314
54333
|
function toPosixPath(input) {
|
|
54315
|
-
return input.split(
|
|
54334
|
+
return input.split(nodePath.sep).join("/");
|
|
54316
54335
|
}
|
|
54317
54336
|
function parentPathOf(input) {
|
|
54318
54337
|
const separatorIndex = input.lastIndexOf("/");
|
|
@@ -54527,7 +54546,7 @@ async function buildWorkspaceIndex(cwd) {
|
|
|
54527
54546
|
const currentDirectories = pendingDirectories;
|
|
54528
54547
|
pendingDirectories = [];
|
|
54529
54548
|
const candidateEntriesByDirectory = (await mapWithConcurrency(currentDirectories, WORKSPACE_SCAN_READDIR_CONCURRENCY, async (relativeDir) => {
|
|
54530
|
-
const absoluteDir = relativeDir ?
|
|
54549
|
+
const absoluteDir = relativeDir ? nodePath.join(cwd, relativeDir) : cwd;
|
|
54531
54550
|
try {
|
|
54532
54551
|
return {
|
|
54533
54552
|
relativeDir,
|
|
@@ -54549,7 +54568,7 @@ async function buildWorkspaceIndex(cwd) {
|
|
|
54549
54568
|
if (!dirent.name || dirent.name === "." || dirent.name === "..") continue;
|
|
54550
54569
|
if (dirent.isDirectory() && IGNORED_DIRECTORY_NAMES.has(dirent.name)) continue;
|
|
54551
54570
|
if (!dirent.isDirectory() && !dirent.isFile()) continue;
|
|
54552
|
-
const relativePath = toPosixPath(relativeDir ?
|
|
54571
|
+
const relativePath = toPosixPath(relativeDir ? nodePath.join(relativeDir, dirent.name) : dirent.name);
|
|
54553
54572
|
if (isPathInIgnoredDirectory(relativePath)) continue;
|
|
54554
54573
|
candidates.push({
|
|
54555
54574
|
dirent,
|
|
@@ -54630,8 +54649,8 @@ async function searchWorkspaceEntries(input) {
|
|
|
54630
54649
|
async function browseFilesystemEntries(input) {
|
|
54631
54650
|
const resolvedInputPath = resolveBrowseTarget(input);
|
|
54632
54651
|
const endsWithSeparator = /[\\/]$/.test(input.partialPath) || input.partialPath === "~";
|
|
54633
|
-
const parentPath = endsWithSeparator ? resolvedInputPath :
|
|
54634
|
-
const prefix = endsWithSeparator ? "" :
|
|
54652
|
+
const parentPath = endsWithSeparator ? resolvedInputPath : nodePath.dirname(resolvedInputPath);
|
|
54653
|
+
const prefix = endsWithSeparator ? "" : nodePath.basename(resolvedInputPath);
|
|
54635
54654
|
const dirents = await fs$1.readdir(parentPath, { withFileTypes: true });
|
|
54636
54655
|
const showHidden = endsWithSeparator || prefix.startsWith(".");
|
|
54637
54656
|
const lowerPrefix = prefix.toLowerCase();
|
|
@@ -54639,7 +54658,7 @@ async function browseFilesystemEntries(input) {
|
|
|
54639
54658
|
parentPath,
|
|
54640
54659
|
entries: dirents.filter((dirent) => dirent.isDirectory() && dirent.name.toLowerCase().startsWith(lowerPrefix) && (showHidden || !dirent.name.startsWith("."))).map((dirent) => ({
|
|
54641
54660
|
name: dirent.name,
|
|
54642
|
-
fullPath:
|
|
54661
|
+
fullPath: nodePath.join(parentPath, dirent.name)
|
|
54643
54662
|
})).sort((left, right) => left.name.localeCompare(right.name))
|
|
54644
54663
|
};
|
|
54645
54664
|
}
|
|
@@ -55959,16 +55978,16 @@ const ProjectionPendingApprovalRepositoryLive = effect(ProjectionPendingApproval
|
|
|
55959
55978
|
//#region src/attachmentPaths.ts
|
|
55960
55979
|
const ATTACHMENTS_ROUTE_PREFIX = "/attachments";
|
|
55961
55980
|
function normalizeAttachmentRelativePath(rawRelativePath) {
|
|
55962
|
-
const normalized =
|
|
55981
|
+
const normalized = nodePath.normalize(rawRelativePath).replace(/^[/\\]+/, "");
|
|
55963
55982
|
if (normalized.length === 0 || normalized.startsWith("..") || normalized.includes("\0")) return null;
|
|
55964
55983
|
return normalized.replace(/\\/g, "/");
|
|
55965
55984
|
}
|
|
55966
55985
|
function resolveAttachmentRelativePath(input) {
|
|
55967
55986
|
const normalizedRelativePath = normalizeAttachmentRelativePath(input.relativePath);
|
|
55968
55987
|
if (!normalizedRelativePath) return null;
|
|
55969
|
-
const attachmentsRoot =
|
|
55970
|
-
const filePath =
|
|
55971
|
-
if (!filePath.startsWith(`${attachmentsRoot}${
|
|
55988
|
+
const attachmentsRoot = nodePath.resolve(nodePath.join(input.stateDir, "attachments"));
|
|
55989
|
+
const filePath = nodePath.resolve(nodePath.join(attachmentsRoot, normalizedRelativePath));
|
|
55990
|
+
if (!filePath.startsWith(`${attachmentsRoot}${nodePath.sep}`)) return null;
|
|
55972
55991
|
return filePath;
|
|
55973
55992
|
}
|
|
55974
55993
|
//#endregion
|
|
@@ -58202,6 +58221,75 @@ function normalizeCommitMessagePrompt(value) {
|
|
|
58202
58221
|
return normalized.length > 0 ? normalized : null;
|
|
58203
58222
|
}
|
|
58204
58223
|
//#endregion
|
|
58224
|
+
//#region ../../packages/contracts/src/settings/auto-roll.ts
|
|
58225
|
+
const DEFAULT_AUTO_ROLL_ENABLED = false;
|
|
58226
|
+
const DEFAULT_AUTO_ROLL_REARM_REMAINING_THRESHOLD = 15;
|
|
58227
|
+
const DEFAULT_AUTO_ROLL_SWITCH_REMAINING_THRESHOLD = 5;
|
|
58228
|
+
const DEFAULT_RESTART_OFFICIAL_CODEX_ON_AUTO_ROLL = false;
|
|
58229
|
+
const DEFAULT_LAUNCH_OFFICIAL_CODEX_WHEN_CLOSED_ON_AUTO_ROLL = false;
|
|
58230
|
+
const DEFAULT_AUTO_ROLL_PRIORITY_ORDER = [];
|
|
58231
|
+
const DEFAULT_LOW_REMAINING_NOTIFICATION_ENABLED = false;
|
|
58232
|
+
const DEFAULT_LOW_REMAINING_NOTIFICATION_THRESHOLD = 1;
|
|
58233
|
+
const AUTO_ROLL_SWITCH_REMAINING_MIN = 0;
|
|
58234
|
+
const AUTO_ROLL_SWITCH_REMAINING_MAX = 50;
|
|
58235
|
+
const AUTO_ROLL_REARM_REMAINING_MAX = 100;
|
|
58236
|
+
const LOW_REMAINING_NOTIFICATION_MIN = 1;
|
|
58237
|
+
const LOW_REMAINING_NOTIFICATION_MAX = 50;
|
|
58238
|
+
function clampNumber(value, min, max) {
|
|
58239
|
+
return Math.min(max, Math.max(min, value));
|
|
58240
|
+
}
|
|
58241
|
+
function resolveFiniteNumber(value, fallback) {
|
|
58242
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
58243
|
+
}
|
|
58244
|
+
function legacyUsedThresholdToRemaining(value, fallbackUsed) {
|
|
58245
|
+
return 100 - clampNumber(resolveFiniteNumber(value, fallbackUsed), 0, 100);
|
|
58246
|
+
}
|
|
58247
|
+
function sanitizeAutoRollSwitchRemainingThreshold(value) {
|
|
58248
|
+
return clampNumber(resolveFiniteNumber(value, 5), 0, 50);
|
|
58249
|
+
}
|
|
58250
|
+
function sanitizeAutoRollRearmRemainingThreshold(value, switchRemainingThreshold) {
|
|
58251
|
+
const sanitizedSwitch = sanitizeAutoRollSwitchRemainingThreshold(switchRemainingThreshold);
|
|
58252
|
+
return clampNumber(resolveFiniteNumber(value, 15), sanitizedSwitch + 1, 100);
|
|
58253
|
+
}
|
|
58254
|
+
function sanitizeAutoRollThresholds(rearmRemainingThreshold, switchRemainingThreshold) {
|
|
58255
|
+
const sanitizedSwitch = sanitizeAutoRollSwitchRemainingThreshold(switchRemainingThreshold);
|
|
58256
|
+
return {
|
|
58257
|
+
rearmRemainingThreshold: sanitizeAutoRollRearmRemainingThreshold(rearmRemainingThreshold, sanitizedSwitch),
|
|
58258
|
+
switchRemainingThreshold: sanitizedSwitch
|
|
58259
|
+
};
|
|
58260
|
+
}
|
|
58261
|
+
function sanitizeAutoRollPriorityOrder(value) {
|
|
58262
|
+
if (!Array.isArray(value)) return [...DEFAULT_AUTO_ROLL_PRIORITY_ORDER];
|
|
58263
|
+
const seen = /* @__PURE__ */ new Set();
|
|
58264
|
+
const normalized = [];
|
|
58265
|
+
for (const item of value) {
|
|
58266
|
+
if (typeof item !== "string") continue;
|
|
58267
|
+
const trimmed = item.trim();
|
|
58268
|
+
if (!trimmed || seen.has(trimmed)) continue;
|
|
58269
|
+
seen.add(trimmed);
|
|
58270
|
+
normalized.push(trimmed);
|
|
58271
|
+
}
|
|
58272
|
+
return normalized;
|
|
58273
|
+
}
|
|
58274
|
+
function sanitizeLowRemainingNotificationThreshold(value) {
|
|
58275
|
+
return clampNumber(resolveFiniteNumber(value, 1), 1, 50);
|
|
58276
|
+
}
|
|
58277
|
+
function normalizeAutoRollSettings(raw) {
|
|
58278
|
+
const enabled = typeof raw?.enabled === "boolean" ? raw.enabled : false;
|
|
58279
|
+
const rawSwitchRemaining = typeof raw?.switchRemainingThreshold === "number" ? raw.switchRemainingThreshold : legacyUsedThresholdToRemaining(raw?.switchThreshold, 95);
|
|
58280
|
+
const { rearmRemainingThreshold: normalizedRearm, switchRemainingThreshold: normalizedSwitch } = sanitizeAutoRollThresholds(typeof raw?.rearmRemainingThreshold === "number" ? raw.rearmRemainingThreshold : legacyUsedThresholdToRemaining(raw?.warningThreshold, 85), rawSwitchRemaining);
|
|
58281
|
+
return {
|
|
58282
|
+
enabled,
|
|
58283
|
+
rearmRemainingThreshold: normalizedRearm,
|
|
58284
|
+
switchRemainingThreshold: normalizedSwitch,
|
|
58285
|
+
restartOfficialCodexOnAutoRoll: raw?.restartOfficialCodexOnAutoRoll === true ? true : false,
|
|
58286
|
+
launchOfficialCodexWhenClosedOnAutoRoll: raw?.launchOfficialCodexWhenClosedOnAutoRoll === true ? true : false,
|
|
58287
|
+
priorityOrder: sanitizeAutoRollPriorityOrder(raw?.priorityOrder),
|
|
58288
|
+
lowRemainingNotificationEnabled: raw?.lowRemainingNotificationEnabled === true ? true : false,
|
|
58289
|
+
lowRemainingNotificationThreshold: sanitizeLowRemainingNotificationThreshold(typeof raw?.lowRemainingNotificationThreshold === "number" ? raw.lowRemainingNotificationThreshold : NaN)
|
|
58290
|
+
};
|
|
58291
|
+
}
|
|
58292
|
+
//#endregion
|
|
58205
58293
|
//#region ../../packages/runtime-app-state/src/storage/documents.ts
|
|
58206
58294
|
const APP_STORAGE_TABLE = "app_storage_documents";
|
|
58207
58295
|
const APP_STORAGE_DB_DIR = "t3-projects";
|
|
@@ -58285,7 +58373,7 @@ function ensureSchema(db, dbPath) {
|
|
|
58285
58373
|
initializedDbPaths.add(dbPath);
|
|
58286
58374
|
}
|
|
58287
58375
|
async function openDatabase(dbPath) {
|
|
58288
|
-
await promises.mkdir(
|
|
58376
|
+
await promises.mkdir(nodePath.dirname(dbPath), { recursive: true });
|
|
58289
58377
|
if (process.versions.bun !== void 0) {
|
|
58290
58378
|
const database = new (await (Function("return import('bun:sqlite')")())).Database(dbPath);
|
|
58291
58379
|
return {
|
|
@@ -58318,7 +58406,7 @@ async function withDatabase(dbPath, task) {
|
|
|
58318
58406
|
throw new Error("App storage database remained busy after retrying.");
|
|
58319
58407
|
}
|
|
58320
58408
|
function resolveAppStorageDbPath(userDataDir) {
|
|
58321
|
-
return
|
|
58409
|
+
return nodePath.join(userDataDir, APP_STORAGE_DB_DIR, APP_STORAGE_DB_NAME);
|
|
58322
58410
|
}
|
|
58323
58411
|
async function readDocument(dbPath, namespace, normalize) {
|
|
58324
58412
|
return withDatabase(dbPath, (db) => {
|
|
@@ -58388,21 +58476,21 @@ function clone(value) {
|
|
|
58388
58476
|
return JSON.parse(JSON.stringify(value));
|
|
58389
58477
|
}
|
|
58390
58478
|
function resolveDefaultUserDataDir() {
|
|
58391
|
-
const home = process.env.HOME || process.env.USERPROFILE ||
|
|
58479
|
+
const home = process.env.HOME || process.env.USERPROFILE || nodeOs.homedir();
|
|
58392
58480
|
if (!home) throw new Error("Unable to resolve home directory for app state.");
|
|
58393
|
-
if (process.platform === "darwin") return
|
|
58481
|
+
if (process.platform === "darwin") return nodePath.join(home, "Library", "Application Support", APP_NAME);
|
|
58394
58482
|
if (process.platform === "win32") {
|
|
58395
58483
|
const appData = process.env.APPDATA;
|
|
58396
|
-
if (appData) return
|
|
58397
|
-
return
|
|
58484
|
+
if (appData) return nodePath.join(appData, APP_NAME);
|
|
58485
|
+
return nodePath.join(home, "AppData", "Roaming", APP_NAME);
|
|
58398
58486
|
}
|
|
58399
|
-
return
|
|
58487
|
+
return nodePath.join(home, ".config", APP_NAME);
|
|
58400
58488
|
}
|
|
58401
58489
|
function getUserDataDir() {
|
|
58402
58490
|
return configuredUserDataDir ?? resolveDefaultUserDataDir();
|
|
58403
58491
|
}
|
|
58404
58492
|
function resolveLegacyAppStatePath() {
|
|
58405
|
-
return
|
|
58493
|
+
return nodePath.join(getUserDataDir(), LEGACY_APP_STATE_FILE);
|
|
58406
58494
|
}
|
|
58407
58495
|
function resolveStorageDbPath() {
|
|
58408
58496
|
return resolveAppStorageDbPath(getUserDataDir());
|
|
@@ -58412,9 +58500,23 @@ function createDefaultAppState() {
|
|
|
58412
58500
|
schemaVersion: 1,
|
|
58413
58501
|
autoRoll: {
|
|
58414
58502
|
enabled: false,
|
|
58415
|
-
|
|
58416
|
-
|
|
58417
|
-
restartOfficialCodexOnAutoRoll: false
|
|
58503
|
+
rearmRemainingThreshold: 15,
|
|
58504
|
+
switchRemainingThreshold: 5,
|
|
58505
|
+
restartOfficialCodexOnAutoRoll: false,
|
|
58506
|
+
launchOfficialCodexWhenClosedOnAutoRoll: false,
|
|
58507
|
+
priorityOrder: [],
|
|
58508
|
+
lowRemainingNotificationEnabled: false,
|
|
58509
|
+
lowRemainingNotificationThreshold: 1
|
|
58510
|
+
},
|
|
58511
|
+
officialCodex: {
|
|
58512
|
+
lastProfileSwitchAt: null,
|
|
58513
|
+
lastProfileSwitchProfileKey: null,
|
|
58514
|
+
lastVerifiedLaunchAt: null,
|
|
58515
|
+
lastVerifiedLaunchProfileKey: null,
|
|
58516
|
+
lastObservedPid: null,
|
|
58517
|
+
lastRestartStatus: null,
|
|
58518
|
+
lastRestartReason: null,
|
|
58519
|
+
instancesByProfileName: {}
|
|
58418
58520
|
},
|
|
58419
58521
|
app: {
|
|
58420
58522
|
lastAppVersion: null,
|
|
@@ -58507,17 +58609,47 @@ function asString$9(value) {
|
|
|
58507
58609
|
const trimmed = value.trim();
|
|
58508
58610
|
return trimmed.length > 0 ? trimmed : null;
|
|
58509
58611
|
}
|
|
58612
|
+
function asNumberOrNull(value) {
|
|
58613
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
58614
|
+
}
|
|
58510
58615
|
function normalizeAppState(raw) {
|
|
58511
58616
|
const defaults = createDefaultAppState();
|
|
58512
58617
|
if (!isRecord$7(raw)) return defaults;
|
|
58618
|
+
const rawAutoRoll = isRecord$7(raw.autoRoll) ? raw.autoRoll : void 0;
|
|
58513
58619
|
const merged = clone(deepMerge(defaults, raw));
|
|
58514
58620
|
merged.schemaVersion = 1;
|
|
58515
|
-
|
|
58516
|
-
if (!
|
|
58517
|
-
|
|
58518
|
-
|
|
58519
|
-
|
|
58520
|
-
|
|
58621
|
+
merged.autoRoll = normalizeAutoRollSettings(rawAutoRoll);
|
|
58622
|
+
if (!isRecord$7(merged.officialCodex)) merged.officialCodex = clone(defaults.officialCodex);
|
|
58623
|
+
merged.officialCodex.lastProfileSwitchAt = asNumberOrNull(merged.officialCodex.lastProfileSwitchAt);
|
|
58624
|
+
merged.officialCodex.lastProfileSwitchProfileKey = asString$9(merged.officialCodex.lastProfileSwitchProfileKey);
|
|
58625
|
+
merged.officialCodex.lastVerifiedLaunchAt = asNumberOrNull(merged.officialCodex.lastVerifiedLaunchAt);
|
|
58626
|
+
merged.officialCodex.lastVerifiedLaunchProfileKey = asString$9(merged.officialCodex.lastVerifiedLaunchProfileKey);
|
|
58627
|
+
merged.officialCodex.lastObservedPid = asNumberOrNull(merged.officialCodex.lastObservedPid);
|
|
58628
|
+
merged.officialCodex.lastRestartStatus = asString$9(merged.officialCodex.lastRestartStatus);
|
|
58629
|
+
merged.officialCodex.lastRestartReason = asString$9(merged.officialCodex.lastRestartReason);
|
|
58630
|
+
if (!isRecord$7(merged.officialCodex.instancesByProfileName)) merged.officialCodex.instancesByProfileName = {};
|
|
58631
|
+
else {
|
|
58632
|
+
const nextInstances = {};
|
|
58633
|
+
for (const [key, value] of Object.entries(merged.officialCodex.instancesByProfileName)) {
|
|
58634
|
+
if (!isRecord$7(value)) continue;
|
|
58635
|
+
const profileName = asString$9(value.profileName) ?? asString$9(key);
|
|
58636
|
+
if (!profileName) continue;
|
|
58637
|
+
nextInstances[profileName] = {
|
|
58638
|
+
profileName,
|
|
58639
|
+
profileKey: asString$9(value.profileKey),
|
|
58640
|
+
profileHome: asString$9(value.profileHome),
|
|
58641
|
+
appPath: asString$9(value.appPath),
|
|
58642
|
+
bundleId: asString$9(value.bundleId),
|
|
58643
|
+
pid: asNumberOrNull(value.pid),
|
|
58644
|
+
appServerPid: asNumberOrNull(value.appServerPid),
|
|
58645
|
+
launchedAt: asNumberOrNull(value.launchedAt),
|
|
58646
|
+
lastVerifiedAt: asNumberOrNull(value.lastVerifiedAt),
|
|
58647
|
+
lastStatus: asString$9(value.lastStatus),
|
|
58648
|
+
lastError: asString$9(value.lastError)
|
|
58649
|
+
};
|
|
58650
|
+
}
|
|
58651
|
+
merged.officialCodex.instancesByProfileName = nextInstances;
|
|
58652
|
+
}
|
|
58521
58653
|
merged.app.lastAppVersion = asString$9(merged.app.lastAppVersion);
|
|
58522
58654
|
merged.app.pendingUpdateVersion = asString$9(merged.app.pendingUpdateVersion);
|
|
58523
58655
|
merged.app.lastProfileName = asString$9(merged.app.lastProfileName);
|
|
@@ -58749,13 +58881,13 @@ const IS_CASE_INSENSITIVE_PLATFORM$2 = process.platform === "darwin" || process.
|
|
|
58749
58881
|
function stripTrailingSeparators$2(input) {
|
|
58750
58882
|
const trimmed = input.trim();
|
|
58751
58883
|
if (trimmed.length === 0) return trimmed;
|
|
58752
|
-
const root =
|
|
58884
|
+
const root = nodePath.parse(trimmed).root;
|
|
58753
58885
|
let next = trimmed;
|
|
58754
58886
|
while (next.length > root.length && /[\\/]+$/.test(next)) next = next.slice(0, -1);
|
|
58755
58887
|
return next;
|
|
58756
58888
|
}
|
|
58757
58889
|
function canonicalizeWorkspaceRoot$1(input) {
|
|
58758
|
-
const resolved =
|
|
58890
|
+
const resolved = nodePath.resolve(input.trim());
|
|
58759
58891
|
return stripTrailingSeparators$2((() => {
|
|
58760
58892
|
try {
|
|
58761
58893
|
return realpathSync.native(resolved);
|
|
@@ -58769,10 +58901,10 @@ function workspaceRootKey$1(input) {
|
|
|
58769
58901
|
return IS_CASE_INSENSITIVE_PLATFORM$2 ? canonical.toLowerCase() : canonical;
|
|
58770
58902
|
}
|
|
58771
58903
|
function resolveAgentChatWorkspaceRoot() {
|
|
58772
|
-
return canonicalizeWorkspaceRoot$1(
|
|
58904
|
+
return canonicalizeWorkspaceRoot$1(nodePath.join(getUserDataDir(), AGENT_CHAT_WORKSPACE_DIRNAME));
|
|
58773
58905
|
}
|
|
58774
58906
|
function resolveAgentChatAgentsPath() {
|
|
58775
|
-
return
|
|
58907
|
+
return nodePath.join(resolveAgentChatWorkspaceRoot(), AGENT_CHAT_AGENTS_FILENAME);
|
|
58776
58908
|
}
|
|
58777
58909
|
function isAgentChatWorkspaceRoot(workspaceRoot) {
|
|
58778
58910
|
return workspaceRootKey$1(workspaceRoot) === workspaceRootKey$1(resolveAgentChatWorkspaceRoot());
|
|
@@ -58786,13 +58918,13 @@ const IS_CASE_INSENSITIVE_PLATFORM$1 = process.platform === "darwin" || process.
|
|
|
58786
58918
|
function stripTrailingSeparators$1(input) {
|
|
58787
58919
|
const trimmed = input.trim();
|
|
58788
58920
|
if (trimmed.length === 0) return trimmed;
|
|
58789
|
-
const root =
|
|
58921
|
+
const root = nodePath.parse(trimmed).root;
|
|
58790
58922
|
let next = trimmed;
|
|
58791
58923
|
while (next.length > root.length && /[\\/]+$/.test(next)) next = next.slice(0, -1);
|
|
58792
58924
|
return next;
|
|
58793
58925
|
}
|
|
58794
58926
|
function canonicalizeWorkspaceRoot(input) {
|
|
58795
|
-
const resolved =
|
|
58927
|
+
const resolved = nodePath.resolve(input.trim());
|
|
58796
58928
|
return stripTrailingSeparators$1((() => {
|
|
58797
58929
|
try {
|
|
58798
58930
|
return realpathSync.native(resolved);
|
|
@@ -58806,54 +58938,15 @@ function workspaceRootKey(input) {
|
|
|
58806
58938
|
return IS_CASE_INSENSITIVE_PLATFORM$1 ? canonical.toLowerCase() : canonical;
|
|
58807
58939
|
}
|
|
58808
58940
|
function resolveGeneralChatWorkspaceRoot() {
|
|
58809
|
-
return canonicalizeWorkspaceRoot(
|
|
58941
|
+
return canonicalizeWorkspaceRoot(nodePath.join(getUserDataDir(), GENERAL_CHAT_WORKSPACE_DIRNAME));
|
|
58810
58942
|
}
|
|
58811
58943
|
function resolveGeneralChatAgentsPath() {
|
|
58812
|
-
return
|
|
58944
|
+
return nodePath.join(resolveGeneralChatWorkspaceRoot(), GENERAL_CHAT_AGENTS_FILENAME);
|
|
58813
58945
|
}
|
|
58814
58946
|
function isGeneralChatWorkspaceRoot(workspaceRoot) {
|
|
58815
58947
|
return workspaceRootKey(workspaceRoot) === workspaceRootKey(resolveGeneralChatWorkspaceRoot());
|
|
58816
58948
|
}
|
|
58817
58949
|
//#endregion
|
|
58818
|
-
//#region ../../packages/contracts/src/settings/auto-roll.ts
|
|
58819
|
-
const DEFAULT_AUTO_ROLL_ENABLED = false;
|
|
58820
|
-
const DEFAULT_AUTO_ROLL_WARNING_THRESHOLD = 85;
|
|
58821
|
-
const DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD = 95;
|
|
58822
|
-
const DEFAULT_RESTART_OFFICIAL_CODEX_ON_AUTO_ROLL = false;
|
|
58823
|
-
const AUTO_ROLL_WARNING_MIN = 50;
|
|
58824
|
-
const AUTO_ROLL_WARNING_MAX = 99;
|
|
58825
|
-
const AUTO_ROLL_SWITCH_MAX = 100;
|
|
58826
|
-
function clampNumber(value, min, max) {
|
|
58827
|
-
return Math.min(max, Math.max(min, value));
|
|
58828
|
-
}
|
|
58829
|
-
function resolveFiniteNumber(value, fallback) {
|
|
58830
|
-
return Number.isFinite(value) ? value : fallback;
|
|
58831
|
-
}
|
|
58832
|
-
function sanitizeAutoRollWarningThreshold(value) {
|
|
58833
|
-
return clampNumber(resolveFiniteNumber(value, 85), 50, AUTO_ROLL_WARNING_MAX);
|
|
58834
|
-
}
|
|
58835
|
-
function sanitizeAutoRollSwitchThreshold(value, warningThreshold) {
|
|
58836
|
-
const sanitizedWarning = sanitizeAutoRollWarningThreshold(warningThreshold);
|
|
58837
|
-
return clampNumber(resolveFiniteNumber(value, 95), sanitizedWarning + 1, AUTO_ROLL_SWITCH_MAX);
|
|
58838
|
-
}
|
|
58839
|
-
function sanitizeAutoRollThresholds(warningThreshold, switchThreshold) {
|
|
58840
|
-
const sanitizedWarning = sanitizeAutoRollWarningThreshold(warningThreshold);
|
|
58841
|
-
return {
|
|
58842
|
-
warningThreshold: sanitizedWarning,
|
|
58843
|
-
switchThreshold: sanitizeAutoRollSwitchThreshold(switchThreshold, sanitizedWarning)
|
|
58844
|
-
};
|
|
58845
|
-
}
|
|
58846
|
-
function normalizeAutoRollSettings(raw) {
|
|
58847
|
-
const enabled = typeof raw?.enabled === "boolean" ? raw.enabled : false;
|
|
58848
|
-
const { warningThreshold: normalizedWarning, switchThreshold: normalizedSwitch } = sanitizeAutoRollThresholds(resolveFiniteNumber(typeof raw?.warningThreshold === "number" ? raw.warningThreshold : NaN, 85), resolveFiniteNumber(typeof raw?.switchThreshold === "number" ? raw.switchThreshold : NaN, 95));
|
|
58849
|
-
return {
|
|
58850
|
-
enabled,
|
|
58851
|
-
warningThreshold: normalizedWarning,
|
|
58852
|
-
switchThreshold: normalizedSwitch,
|
|
58853
|
-
restartOfficialCodexOnAutoRoll: raw?.restartOfficialCodexOnAutoRoll === true ? true : false
|
|
58854
|
-
};
|
|
58855
|
-
}
|
|
58856
|
-
//#endregion
|
|
58857
58950
|
//#region ../../packages/runtime-codex/src/codex/settings.ts
|
|
58858
58951
|
function asString$8(value) {
|
|
58859
58952
|
if (typeof value !== "string") return null;
|
|
@@ -58891,12 +58984,6 @@ function parseStoredLicense(raw) {
|
|
|
58891
58984
|
};
|
|
58892
58985
|
return Boolean(license.licenseKey || license.purchaseEmail || license.lastVerifiedAt || license.nextCheckAt || license.lastVerificationError || license.status) ? license : null;
|
|
58893
58986
|
}
|
|
58894
|
-
async function getLastProfileName() {
|
|
58895
|
-
return asString$8((await getAppState()).app.lastProfileName);
|
|
58896
|
-
}
|
|
58897
|
-
async function persistLastProfileName(profileName) {
|
|
58898
|
-
await patchAppState({ app: { lastProfileName: asString$8(profileName) } });
|
|
58899
|
-
}
|
|
58900
58987
|
async function getStoredLicense() {
|
|
58901
58988
|
return parseStoredLicense((await getAppState()).license);
|
|
58902
58989
|
}
|
|
@@ -58927,9 +59014,13 @@ async function persistAutoRollSettings(settings) {
|
|
|
58927
59014
|
const normalized = normalizeAutoRollSettings(settings ?? void 0);
|
|
58928
59015
|
await patchAppState({ autoRoll: {
|
|
58929
59016
|
enabled: normalized.enabled,
|
|
58930
|
-
|
|
58931
|
-
|
|
58932
|
-
restartOfficialCodexOnAutoRoll: normalized.restartOfficialCodexOnAutoRoll
|
|
59017
|
+
rearmRemainingThreshold: normalized.rearmRemainingThreshold,
|
|
59018
|
+
switchRemainingThreshold: normalized.switchRemainingThreshold,
|
|
59019
|
+
restartOfficialCodexOnAutoRoll: normalized.restartOfficialCodexOnAutoRoll,
|
|
59020
|
+
launchOfficialCodexWhenClosedOnAutoRoll: normalized.launchOfficialCodexWhenClosedOnAutoRoll,
|
|
59021
|
+
priorityOrder: normalized.priorityOrder,
|
|
59022
|
+
lowRemainingNotificationEnabled: normalized.lowRemainingNotificationEnabled,
|
|
59023
|
+
lowRemainingNotificationThreshold: normalized.lowRemainingNotificationThreshold
|
|
58933
59024
|
} });
|
|
58934
59025
|
}
|
|
58935
59026
|
async function readCodexSettingsJsonRaw() {
|
|
@@ -58967,9 +59058,13 @@ async function writeCodexSettingsJsonRaw(payload) {
|
|
|
58967
59058
|
},
|
|
58968
59059
|
autoRoll: autoRoll ? {
|
|
58969
59060
|
enabled: autoRoll.enabled,
|
|
58970
|
-
|
|
58971
|
-
|
|
58972
|
-
restartOfficialCodexOnAutoRoll: autoRoll.restartOfficialCodexOnAutoRoll
|
|
59061
|
+
rearmRemainingThreshold: autoRoll.rearmRemainingThreshold,
|
|
59062
|
+
switchRemainingThreshold: autoRoll.switchRemainingThreshold,
|
|
59063
|
+
restartOfficialCodexOnAutoRoll: autoRoll.restartOfficialCodexOnAutoRoll,
|
|
59064
|
+
launchOfficialCodexWhenClosedOnAutoRoll: autoRoll.launchOfficialCodexWhenClosedOnAutoRoll,
|
|
59065
|
+
priorityOrder: autoRoll.priorityOrder,
|
|
59066
|
+
lowRemainingNotificationEnabled: autoRoll.lowRemainingNotificationEnabled,
|
|
59067
|
+
lowRemainingNotificationThreshold: autoRoll.lowRemainingNotificationThreshold
|
|
58973
59068
|
} : void 0,
|
|
58974
59069
|
license: license ? {
|
|
58975
59070
|
licenseKey: license.licenseKey ?? null,
|
|
@@ -59007,7 +59102,7 @@ function normalizeSecret(value) {
|
|
|
59007
59102
|
return typeof value === "string" ? value.trim() : "";
|
|
59008
59103
|
}
|
|
59009
59104
|
async function readLegacyLicenseSecret() {
|
|
59010
|
-
const legacyPath =
|
|
59105
|
+
const legacyPath = nodePath.join(getUserDataDir(), LEGACY_LICENSE_SECRET_FILE$1);
|
|
59011
59106
|
try {
|
|
59012
59107
|
return normalizeSecret(await promises.readFile(legacyPath, "utf8"));
|
|
59013
59108
|
} catch (error) {
|
|
@@ -59356,13 +59451,13 @@ const IS_CASE_INSENSITIVE_PLATFORM = process.platform === "darwin" || process.pl
|
|
|
59356
59451
|
function stripTrailingSeparators(input) {
|
|
59357
59452
|
const resolved = input.trim();
|
|
59358
59453
|
if (resolved.length === 0) return resolved;
|
|
59359
|
-
const root =
|
|
59454
|
+
const root = nodePath.parse(resolved).root;
|
|
59360
59455
|
let next = resolved;
|
|
59361
59456
|
while (next.length > root.length && /[\\/]+$/.test(next)) next = next.slice(0, -1);
|
|
59362
59457
|
return next;
|
|
59363
59458
|
}
|
|
59364
59459
|
function canonicalizeProjectRoot(input) {
|
|
59365
|
-
const resolved =
|
|
59460
|
+
const resolved = nodePath.resolve(input.trim());
|
|
59366
59461
|
return stripTrailingSeparators((() => {
|
|
59367
59462
|
try {
|
|
59368
59463
|
return realpathSync.native(resolved);
|
|
@@ -59543,14 +59638,14 @@ let cachedStatus = null;
|
|
|
59543
59638
|
function getPathHintEntries() {
|
|
59544
59639
|
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
59545
59640
|
const homeEntries = homeDir ? [
|
|
59546
|
-
|
|
59547
|
-
|
|
59548
|
-
|
|
59549
|
-
|
|
59550
|
-
|
|
59551
|
-
|
|
59641
|
+
nodePath.join(homeDir, ".local", "bin"),
|
|
59642
|
+
nodePath.join(homeDir, ".fnm", "aliases", "default", "bin"),
|
|
59643
|
+
nodePath.join(homeDir, ".fnm", "current", "bin"),
|
|
59644
|
+
nodePath.join(homeDir, ".volta", "bin"),
|
|
59645
|
+
nodePath.join(homeDir, ".asdf", "shims"),
|
|
59646
|
+
nodePath.join(homeDir, ".bun", "bin")
|
|
59552
59647
|
] : [];
|
|
59553
|
-
const configuredHints = (process.env.CODEX_PATH_HINTS ?? "").split(
|
|
59648
|
+
const configuredHints = (process.env.CODEX_PATH_HINTS ?? "").split(nodePath.delimiter).map((entry) => entry.trim()).filter(Boolean);
|
|
59554
59649
|
return [
|
|
59555
59650
|
"/opt/homebrew/bin",
|
|
59556
59651
|
"/usr/local/bin",
|
|
@@ -59560,7 +59655,7 @@ function getPathHintEntries() {
|
|
|
59560
59655
|
}
|
|
59561
59656
|
function fileExists$1(candidate) {
|
|
59562
59657
|
if (!candidate) return null;
|
|
59563
|
-
const normalized =
|
|
59658
|
+
const normalized = nodePath.resolve(candidate);
|
|
59564
59659
|
try {
|
|
59565
59660
|
if (statSync(normalized).isFile()) return normalized;
|
|
59566
59661
|
} catch {}
|
|
@@ -59577,7 +59672,7 @@ function resolveCodexFromEnv() {
|
|
|
59577
59672
|
}
|
|
59578
59673
|
function resolveCodexFromPath() {
|
|
59579
59674
|
const pathValue = process.env.PATH ?? "";
|
|
59580
|
-
const entries = Array.from(new Set([...getPathHintEntries(), ...pathValue.split(
|
|
59675
|
+
const entries = Array.from(new Set([...getPathHintEntries(), ...pathValue.split(nodePath.delimiter).map((entry) => entry.trim()).filter(Boolean)].filter(Boolean)));
|
|
59581
59676
|
const names = process.platform === "win32" ? [
|
|
59582
59677
|
"codex.exe",
|
|
59583
59678
|
"codex.cmd",
|
|
@@ -59585,16 +59680,16 @@ function resolveCodexFromPath() {
|
|
|
59585
59680
|
"codex"
|
|
59586
59681
|
] : ["codex"];
|
|
59587
59682
|
for (const entry of entries) for (const name of names) {
|
|
59588
|
-
const candidate = fileExists$1(
|
|
59683
|
+
const candidate = fileExists$1(nodePath.join(entry, name));
|
|
59589
59684
|
if (candidate) return candidate;
|
|
59590
59685
|
}
|
|
59591
59686
|
return null;
|
|
59592
59687
|
}
|
|
59593
59688
|
function resolvePackageJsonPath(cliPath) {
|
|
59594
|
-
const normalized =
|
|
59595
|
-
const binDir =
|
|
59596
|
-
const packageDir =
|
|
59597
|
-
return fileExists$1(
|
|
59689
|
+
const normalized = nodePath.resolve(cliPath);
|
|
59690
|
+
const binDir = nodePath.dirname(normalized);
|
|
59691
|
+
const packageDir = nodePath.basename(binDir) === "bin" ? nodePath.dirname(binDir) : nodePath.dirname(normalized);
|
|
59692
|
+
return fileExists$1(nodePath.join(packageDir, "package.json"));
|
|
59598
59693
|
}
|
|
59599
59694
|
function readCodexCliVersion(cliPath) {
|
|
59600
59695
|
const packageJsonPath = resolvePackageJsonPath(cliPath);
|
|
@@ -59685,17 +59780,17 @@ function resolveNodeRuntime(env) {
|
|
|
59685
59780
|
...env
|
|
59686
59781
|
};
|
|
59687
59782
|
const homeDir = runtimeEnv.HOME ?? runtimeEnv.USERPROFILE ?? "";
|
|
59688
|
-
const pathHints = (runtimeEnv.CODEX_PATH_HINTS ?? process.env.CODEX_PATH_HINTS ?? "").split(
|
|
59783
|
+
const pathHints = (runtimeEnv.CODEX_PATH_HINTS ?? process.env.CODEX_PATH_HINTS ?? "").split(nodePath.delimiter).map((entry) => entry.trim()).filter(Boolean);
|
|
59689
59784
|
const extraPathEntries = [
|
|
59690
59785
|
"/usr/local/bin",
|
|
59691
59786
|
"/opt/homebrew/bin",
|
|
59692
|
-
|
|
59693
|
-
|
|
59694
|
-
|
|
59787
|
+
nodePath.join(homeDir, ".local", "bin"),
|
|
59788
|
+
nodePath.join(homeDir, ".fnm", "aliases", "default", "bin"),
|
|
59789
|
+
nodePath.join(homeDir, ".fnm", "current", "bin"),
|
|
59695
59790
|
...pathHints
|
|
59696
59791
|
].filter(Boolean);
|
|
59697
59792
|
const currentPath = runtimeEnv.PATH ?? "";
|
|
59698
|
-
runtimeEnv.PATH = Array.from(new Set([...extraPathEntries, ...currentPath.split(
|
|
59793
|
+
runtimeEnv.PATH = Array.from(new Set([...extraPathEntries, ...currentPath.split(nodePath.delimiter).filter(Boolean)])).join(nodePath.delimiter);
|
|
59699
59794
|
const candidates = Array.from(new Set([
|
|
59700
59795
|
runtimeEnv.CODEX_NODE_RUNTIME?.trim(),
|
|
59701
59796
|
runtimeEnv.CODEX_NODE_BIN?.trim(),
|
|
@@ -59732,11 +59827,11 @@ function buildCodexCommand$1(binaryPath, args, env) {
|
|
|
59732
59827
|
//#endregion
|
|
59733
59828
|
//#region ../../packages/runtime-codex/src/codex/home.ts
|
|
59734
59829
|
function resolveHomeDir$1() {
|
|
59735
|
-
return process.env.HOME || process.env.USERPROFILE ||
|
|
59830
|
+
return process.env.HOME || process.env.USERPROFILE || nodeOs.homedir();
|
|
59736
59831
|
}
|
|
59737
59832
|
function expandHomePrefix(input, homeDir) {
|
|
59738
59833
|
if (input === "~") return homeDir;
|
|
59739
|
-
if (input.startsWith("~/") || input.startsWith("~\\")) return
|
|
59834
|
+
if (input.startsWith("~/") || input.startsWith("~\\")) return nodePath.join(homeDir, input.slice(2));
|
|
59740
59835
|
return input;
|
|
59741
59836
|
}
|
|
59742
59837
|
function normalizePathCandidate(value) {
|
|
@@ -59747,13 +59842,10 @@ function normalizePathCandidate(value) {
|
|
|
59747
59842
|
function normalizeCodexHomePath(input) {
|
|
59748
59843
|
const configured = normalizePathCandidate(input);
|
|
59749
59844
|
if (!configured) return null;
|
|
59750
|
-
return
|
|
59845
|
+
return nodePath.resolve(expandHomePrefix(configured, resolveHomeDir$1()));
|
|
59751
59846
|
}
|
|
59752
59847
|
function resolveDefaultCodexHomeDir() {
|
|
59753
|
-
return
|
|
59754
|
-
}
|
|
59755
|
-
function resolveProfileCodexHomeDir(profileName) {
|
|
59756
|
-
return path.join(getUserDataDir(), "profile-homes", profileName);
|
|
59848
|
+
return nodePath.join(resolveHomeDir$1(), ".codex");
|
|
59757
59849
|
}
|
|
59758
59850
|
function resolveCodexHomeDir(options) {
|
|
59759
59851
|
return normalizeCodexHomePath(options?.codexHomePath) ?? resolveDefaultCodexHomeDir();
|
|
@@ -59765,14 +59857,7 @@ async function resolveCodexRuntimeContext(options) {
|
|
|
59765
59857
|
profileName: null,
|
|
59766
59858
|
source: "explicit"
|
|
59767
59859
|
};
|
|
59768
|
-
const
|
|
59769
|
-
const profileName = normalizePathCandidate(state.app.lastProfileName);
|
|
59770
|
-
if (profileName && state.profilesByName[profileName]) return {
|
|
59771
|
-
codexHomePath: resolveProfileCodexHomeDir(profileName),
|
|
59772
|
-
profileName,
|
|
59773
|
-
source: "profile"
|
|
59774
|
-
};
|
|
59775
|
-
const configured = normalizeCodexHomePath(state.runtimeSettings?.codexHome);
|
|
59860
|
+
const configured = normalizeCodexHomePath((await getAppState()).runtimeSettings?.codexHome);
|
|
59776
59861
|
if (configured) return {
|
|
59777
59862
|
codexHomePath: configured,
|
|
59778
59863
|
profileName: null,
|
|
@@ -59849,19 +59934,19 @@ function buildCliEnv(codexPath, overrides = {}) {
|
|
|
59849
59934
|
...overrides
|
|
59850
59935
|
};
|
|
59851
59936
|
const currentPath = env.PATH ?? "";
|
|
59852
|
-
const codexDir = path
|
|
59937
|
+
const codexDir = path.dirname(codexPath);
|
|
59853
59938
|
const homeDir = process.env.HOME ?? "";
|
|
59854
|
-
const extraPathHints = (process.env.CODEX_PATH_HINTS ?? "").split(path
|
|
59939
|
+
const extraPathHints = (process.env.CODEX_PATH_HINTS ?? "").split(path.delimiter).map((entry) => entry.trim()).filter(Boolean);
|
|
59855
59940
|
const segments = [...[
|
|
59856
59941
|
codexDir,
|
|
59857
59942
|
"/usr/local/bin",
|
|
59858
59943
|
"/opt/homebrew/bin",
|
|
59859
|
-
path
|
|
59860
|
-
path
|
|
59861
|
-
path
|
|
59944
|
+
path.join(homeDir, ".local", "bin"),
|
|
59945
|
+
path.join(homeDir, ".fnm", "aliases", "default", "bin"),
|
|
59946
|
+
path.join(homeDir, ".fnm", "current", "bin"),
|
|
59862
59947
|
...extraPathHints
|
|
59863
|
-
].filter(Boolean), ...currentPath.split(path
|
|
59864
|
-
env.PATH = Array.from(new Set(segments)).join(path
|
|
59948
|
+
].filter(Boolean), ...currentPath.split(path.delimiter).filter(Boolean)];
|
|
59949
|
+
env.PATH = Array.from(new Set(segments)).join(path.delimiter);
|
|
59865
59950
|
return env;
|
|
59866
59951
|
}
|
|
59867
59952
|
//#endregion
|
|
@@ -60818,7 +60903,7 @@ async function getActiveProfileContext() {
|
|
|
60818
60903
|
const profileKey = toProfileStorageKey(profileName);
|
|
60819
60904
|
return {
|
|
60820
60905
|
profileKey,
|
|
60821
|
-
parityStoreDir:
|
|
60906
|
+
parityStoreDir: nodePath.join(getUserDataDir(), PROFILE_ROOT_DIR, profileKey, PROFILE_PARITY_DIR)
|
|
60822
60907
|
};
|
|
60823
60908
|
}
|
|
60824
60909
|
async function fileExists(filePath) {
|
|
@@ -60830,10 +60915,10 @@ async function fileExists(filePath) {
|
|
|
60830
60915
|
}
|
|
60831
60916
|
}
|
|
60832
60917
|
function legacyStorePath() {
|
|
60833
|
-
return
|
|
60918
|
+
return nodePath.join(getUserDataDir(), LEGACY_STORE_FILE);
|
|
60834
60919
|
}
|
|
60835
60920
|
function legacyMigrationMarkerPath() {
|
|
60836
|
-
return
|
|
60921
|
+
return nodePath.join(getUserDataDir(), LEGACY_MIGRATION_MARKER_FILE);
|
|
60837
60922
|
}
|
|
60838
60923
|
async function readLegacyMigrationMarker() {
|
|
60839
60924
|
try {
|
|
@@ -60852,13 +60937,13 @@ async function migrateLegacyStoreIfNeeded(scopedStorePath) {
|
|
|
60852
60937
|
if (!await fileExists(legacyPath)) return;
|
|
60853
60938
|
const marker = await readLegacyMigrationMarker();
|
|
60854
60939
|
if (marker?.migratedToProfileKey && marker.migratedToProfileKey !== context.profileKey) return;
|
|
60855
|
-
await fs$1.mkdir(
|
|
60940
|
+
await fs$1.mkdir(nodePath.dirname(scopedStorePath), { recursive: true });
|
|
60856
60941
|
await fs$1.copyFile(legacyPath, scopedStorePath);
|
|
60857
60942
|
}
|
|
60858
60943
|
async function storePath() {
|
|
60859
60944
|
const context = await getActiveProfileContext();
|
|
60860
60945
|
await fs$1.mkdir(context.parityStoreDir, { recursive: true });
|
|
60861
|
-
const scopedPath =
|
|
60946
|
+
const scopedPath = nodePath.join(context.parityStoreDir, STORE_FILE);
|
|
60862
60947
|
await migrateLegacyStoreIfNeeded(scopedPath);
|
|
60863
60948
|
return scopedPath;
|
|
60864
60949
|
}
|
|
@@ -60869,7 +60954,7 @@ function toWorkspaceName(inputPath) {
|
|
|
60869
60954
|
function sanitizeWorkspace(entry) {
|
|
60870
60955
|
if (!entry || typeof entry !== "object") return null;
|
|
60871
60956
|
const id = typeof entry.id === "string" && entry.id.trim() ? entry.id.trim() : null;
|
|
60872
|
-
const workspacePath = typeof entry.path === "string" && entry.path.trim() ?
|
|
60957
|
+
const workspacePath = typeof entry.path === "string" && entry.path.trim() ? nodePath.resolve(entry.path) : null;
|
|
60873
60958
|
if (!id || !workspacePath) return null;
|
|
60874
60959
|
const name = typeof entry.name === "string" && entry.name.trim() ? entry.name.trim() : toWorkspaceName(workspacePath);
|
|
60875
60960
|
const branch = entry.worktree && typeof entry.worktree === "object" && typeof entry.worktree.branch === "string" ? entry.worktree.branch.trim() || "main" : "main";
|
|
@@ -60988,7 +61073,7 @@ function ensureThreadEntry(store, threadId) {
|
|
|
60988
61073
|
return created;
|
|
60989
61074
|
}
|
|
60990
61075
|
function resolveOverridesDbPath(stateDir) {
|
|
60991
|
-
return
|
|
61076
|
+
return nodePath.join(stateDir, "state.sqlite");
|
|
60992
61077
|
}
|
|
60993
61078
|
async function readExternalThreadOverrides(stateDir) {
|
|
60994
61079
|
return await readDocument(resolveOverridesDbPath(stateDir), EXTERNAL_THREAD_OVERRIDES_DOCUMENT, (value) => {
|
|
@@ -61310,7 +61395,7 @@ function collectExternalCodexThreadSyncDirectoryWatchPaths(rootPath) {
|
|
|
61310
61395
|
while (stack.length > 0) {
|
|
61311
61396
|
const currentPath = stack.pop();
|
|
61312
61397
|
if (!currentPath) continue;
|
|
61313
|
-
const resolvedCurrentPath =
|
|
61398
|
+
const resolvedCurrentPath = nodePath.resolve(currentPath);
|
|
61314
61399
|
if (seenPaths.has(resolvedCurrentPath)) continue;
|
|
61315
61400
|
seenPaths.add(resolvedCurrentPath);
|
|
61316
61401
|
let stat;
|
|
@@ -61329,7 +61414,7 @@ function collectExternalCodexThreadSyncDirectoryWatchPaths(rootPath) {
|
|
|
61329
61414
|
}
|
|
61330
61415
|
for (const entry of entries) {
|
|
61331
61416
|
if (!entry.isDirectory()) continue;
|
|
61332
|
-
stack.push(
|
|
61417
|
+
stack.push(nodePath.join(resolvedCurrentPath, entry.name));
|
|
61333
61418
|
}
|
|
61334
61419
|
}
|
|
61335
61420
|
return collectedPaths;
|
|
@@ -61338,7 +61423,7 @@ function collectExternalCodexThreadSyncWatchTargets(homePath) {
|
|
|
61338
61423
|
const recursive = supportsRecursiveExternalCodexThreadSyncWatcher();
|
|
61339
61424
|
const targets = [];
|
|
61340
61425
|
for (const relativePath of EXTERNAL_SYNC_WATCH_DIRECTORY_NAMES) {
|
|
61341
|
-
const absolutePath =
|
|
61426
|
+
const absolutePath = nodePath.join(homePath, relativePath);
|
|
61342
61427
|
if (!fs.existsSync(absolutePath)) continue;
|
|
61343
61428
|
if (recursive) {
|
|
61344
61429
|
targets.push({
|
|
@@ -61350,7 +61435,7 @@ function collectExternalCodexThreadSyncWatchTargets(homePath) {
|
|
|
61350
61435
|
}
|
|
61351
61436
|
for (const watchPath of collectExternalCodexThreadSyncDirectoryWatchPaths(absolutePath)) targets.push({
|
|
61352
61437
|
watchPath,
|
|
61353
|
-
label:
|
|
61438
|
+
label: nodePath.relative(homePath, watchPath) || ".",
|
|
61354
61439
|
recursive: false
|
|
61355
61440
|
});
|
|
61356
61441
|
}
|
|
@@ -61362,7 +61447,7 @@ function collectExternalCodexThreadSyncWatchTargets(homePath) {
|
|
|
61362
61447
|
return targets;
|
|
61363
61448
|
}
|
|
61364
61449
|
function startExternalCodexThreadSyncWatcher(homePath) {
|
|
61365
|
-
const resolvedHomePath =
|
|
61450
|
+
const resolvedHomePath = nodePath.resolve(homePath);
|
|
61366
61451
|
if (externalCodexThreadSyncWatchers.length > 0 && externalCodexThreadSyncWatcherHomePath === resolvedHomePath) return;
|
|
61367
61452
|
closeExternalCodexThreadSyncWatcher();
|
|
61368
61453
|
try {
|
|
@@ -61393,7 +61478,7 @@ function startExternalCodexThreadSyncWatcher(homePath) {
|
|
|
61393
61478
|
}
|
|
61394
61479
|
}
|
|
61395
61480
|
function resolveStateDbPath(stateDir) {
|
|
61396
|
-
return
|
|
61481
|
+
return nodePath.join(stateDir, "state.sqlite");
|
|
61397
61482
|
}
|
|
61398
61483
|
function asString$6(value) {
|
|
61399
61484
|
return typeof value === "string" ? value : value == null ? "" : String(value);
|
|
@@ -61680,7 +61765,7 @@ function resolveLegacyFilePath(source) {
|
|
|
61680
61765
|
if (normalized.startsWith("~/")) {
|
|
61681
61766
|
const home = process.env.HOME?.trim();
|
|
61682
61767
|
if (!home) return null;
|
|
61683
|
-
return
|
|
61768
|
+
return nodePath.join(home, normalized.slice(2));
|
|
61684
61769
|
}
|
|
61685
61770
|
return normalized;
|
|
61686
61771
|
}
|
|
@@ -61716,7 +61801,7 @@ async function materializeLegacyImageAttachment(input) {
|
|
|
61716
61801
|
mimeType = Mime_default.getType(filePath) ?? "";
|
|
61717
61802
|
if (!mimeType.startsWith("image/")) return null;
|
|
61718
61803
|
bytes = await fs$1.readFile(filePath);
|
|
61719
|
-
name =
|
|
61804
|
+
name = nodePath.basename(filePath) || `image${inferImageExtension({ mimeType })}`;
|
|
61720
61805
|
}
|
|
61721
61806
|
if (bytes.byteLength === 0 || bytes.byteLength > 10485760) return null;
|
|
61722
61807
|
const attachmentId = createDeterministicLegacyAttachmentId(input.threadId, input.attachmentKey);
|
|
@@ -61733,7 +61818,7 @@ async function materializeLegacyImageAttachment(input) {
|
|
|
61733
61818
|
attachment
|
|
61734
61819
|
});
|
|
61735
61820
|
if (!attachmentPath) return null;
|
|
61736
|
-
await fs$1.mkdir(
|
|
61821
|
+
await fs$1.mkdir(nodePath.dirname(attachmentPath), { recursive: true });
|
|
61737
61822
|
await fs$1.writeFile(attachmentPath, bytes);
|
|
61738
61823
|
return attachment;
|
|
61739
61824
|
} catch {
|
|
@@ -62052,7 +62137,7 @@ async function readImportMarker(stateDir) {
|
|
|
62052
62137
|
return { version };
|
|
62053
62138
|
});
|
|
62054
62139
|
if (stored) return stored;
|
|
62055
|
-
const markerPath =
|
|
62140
|
+
const markerPath = nodePath.join(stateDir, EXTERNAL_SYNC_MARKER_FILE);
|
|
62056
62141
|
try {
|
|
62057
62142
|
const raw = await fs$1.readFile(markerPath, "utf8");
|
|
62058
62143
|
const version = asFiniteNumber(asRecord$3(JSON.parse(raw))?.version);
|
|
@@ -62111,7 +62196,7 @@ function shouldReadLegacyThreadForImport(input) {
|
|
|
62111
62196
|
}
|
|
62112
62197
|
async function writeImportMarker(stateDir, payload) {
|
|
62113
62198
|
await writeDocument(resolveStateDbPath(stateDir), EXTERNAL_SYNC_MARKER_DOCUMENT, payload);
|
|
62114
|
-
await fs$1.rm(
|
|
62199
|
+
await fs$1.rm(nodePath.join(stateDir, EXTERNAL_SYNC_MARKER_FILE), { force: true }).catch(() => void 0);
|
|
62115
62200
|
}
|
|
62116
62201
|
function resolveLegacyThreadTimestamps(thread) {
|
|
62117
62202
|
const createdAtMs = toEpochMs(thread.createdAt ?? thread.updatedAt, Date.now());
|
|
@@ -65115,7 +65200,7 @@ var RotatingFileSink = class {
|
|
|
65115
65200
|
this.maxBytes = options.maxBytes;
|
|
65116
65201
|
this.maxFiles = options.maxFiles;
|
|
65117
65202
|
this.throwOnError = options.throwOnError ?? false;
|
|
65118
|
-
fs.mkdirSync(
|
|
65203
|
+
fs.mkdirSync(nodePath.dirname(this.filePath), { recursive: true });
|
|
65119
65204
|
this.pruneOverflowBackups();
|
|
65120
65205
|
this.currentSize = this.readCurrentSize();
|
|
65121
65206
|
}
|
|
@@ -65150,13 +65235,13 @@ var RotatingFileSink = class {
|
|
|
65150
65235
|
}
|
|
65151
65236
|
pruneOverflowBackups() {
|
|
65152
65237
|
try {
|
|
65153
|
-
const dir =
|
|
65154
|
-
const baseName =
|
|
65238
|
+
const dir = nodePath.dirname(this.filePath);
|
|
65239
|
+
const baseName = nodePath.basename(this.filePath);
|
|
65155
65240
|
for (const entry of fs.readdirSync(dir)) {
|
|
65156
65241
|
if (!entry.startsWith(`${baseName}.`)) continue;
|
|
65157
65242
|
const suffix = Number(entry.slice(baseName.length + 1));
|
|
65158
65243
|
if (!Number.isInteger(suffix) || suffix <= this.maxFiles) continue;
|
|
65159
|
-
fs.rmSync(
|
|
65244
|
+
fs.rmSync(nodePath.join(dir, entry), { force: true });
|
|
65160
65245
|
}
|
|
65161
65246
|
} catch {
|
|
65162
65247
|
if (this.throwOnError) throw new Error(`Failed to prune log backups for ${this.filePath}`);
|
|
@@ -65296,7 +65381,7 @@ function makeEventNdjsonLogger(filePath, options) {
|
|
|
65296
65381
|
const streamLabel = resolveStreamLabel(options.stream);
|
|
65297
65382
|
const directoryReady = yield* sync(() => {
|
|
65298
65383
|
try {
|
|
65299
|
-
fs.mkdirSync(
|
|
65384
|
+
fs.mkdirSync(nodePath.dirname(filePath), { recursive: true });
|
|
65300
65385
|
return true;
|
|
65301
65386
|
} catch (error) {
|
|
65302
65387
|
return {
|
|
@@ -65319,7 +65404,7 @@ function makeEventNdjsonLogger(filePath, options) {
|
|
|
65319
65404
|
const existing = threadWriters.get(threadSegment);
|
|
65320
65405
|
if (existing) return existing;
|
|
65321
65406
|
const writer = yield* makeThreadWriter({
|
|
65322
|
-
filePath:
|
|
65407
|
+
filePath: nodePath.join(nodePath.dirname(filePath), `${threadSegment}.log`),
|
|
65323
65408
|
maxBytes,
|
|
65324
65409
|
maxFiles,
|
|
65325
65410
|
batchWindowMs,
|
|
@@ -65536,12 +65621,14 @@ function toUserInputQuestions(payload) {
|
|
|
65536
65621
|
const id = asString$3(question.id)?.trim();
|
|
65537
65622
|
const header = asString$3(question.header)?.trim();
|
|
65538
65623
|
const prompt = asString$3(question.question)?.trim();
|
|
65624
|
+
const multiSelect = question.multiSelect === true || question.multi_select === true;
|
|
65539
65625
|
if (!id || !header || !prompt || !options || options.length === 0) return;
|
|
65540
65626
|
return {
|
|
65541
65627
|
id,
|
|
65542
65628
|
header,
|
|
65543
65629
|
question: prompt,
|
|
65544
|
-
options
|
|
65630
|
+
options,
|
|
65631
|
+
multiSelect
|
|
65545
65632
|
};
|
|
65546
65633
|
}).filter((question) => question !== void 0);
|
|
65547
65634
|
return parsedQuestions.length > 0 ? parsedQuestions : void 0;
|
|
@@ -66843,7 +66930,7 @@ function normalizeShellCommand(value) {
|
|
|
66843
66930
|
}
|
|
66844
66931
|
function shellCandidateFromCommand(command) {
|
|
66845
66932
|
if (!command || command.length === 0) return null;
|
|
66846
|
-
const shellName =
|
|
66933
|
+
const shellName = nodePath.basename(command).toLowerCase();
|
|
66847
66934
|
if (process.platform !== "win32" && shellName === "zsh") return {
|
|
66848
66935
|
shell: command,
|
|
66849
66936
|
args: ["-o", "nopromptsp"]
|
|
@@ -67025,7 +67112,7 @@ var TerminalManagerRuntime = class extends EventEmitter {
|
|
|
67025
67112
|
this.subprocessPollInFlight = false;
|
|
67026
67113
|
this.killEscalationTimers = /* @__PURE__ */ new Map();
|
|
67027
67114
|
this.logger = createLogger("terminal");
|
|
67028
|
-
this.logsDir = options.logsDir ??
|
|
67115
|
+
this.logsDir = options.logsDir ?? nodePath.resolve(process.cwd(), ".logs", "terminals");
|
|
67029
67116
|
this.historyLineLimit = options.historyLineLimit ?? DEFAULT_HISTORY_LINE_LIMIT;
|
|
67030
67117
|
this.ptyAdapter = options.ptyAdapter;
|
|
67031
67118
|
this.shellResolver = options.shellResolver ?? defaultShellResolver;
|
|
@@ -67605,7 +67692,7 @@ var TerminalManagerRuntime = class extends EventEmitter {
|
|
|
67605
67692
|
async deleteAllHistoryForThread(threadId) {
|
|
67606
67693
|
const threadPrefix = `${toSafeThreadId(threadId)}_`;
|
|
67607
67694
|
try {
|
|
67608
|
-
const removals = (await fs.promises.readdir(this.logsDir, { withFileTypes: true })).filter((entry) => entry.isFile()).map((entry) => entry.name).filter((name) => name === `${toSafeThreadId(threadId)}.log` || name === `${legacySafeThreadId(threadId)}.log` || name.startsWith(threadPrefix)).map((name) => fs.promises.rm(
|
|
67695
|
+
const removals = (await fs.promises.readdir(this.logsDir, { withFileTypes: true })).filter((entry) => entry.isFile()).map((entry) => entry.name).filter((name) => name === `${toSafeThreadId(threadId)}.log` || name === `${legacySafeThreadId(threadId)}.log` || name.startsWith(threadPrefix)).map((name) => fs.promises.rm(nodePath.join(this.logsDir, name), { force: true }));
|
|
67609
67696
|
await Promise.all(removals);
|
|
67610
67697
|
} catch (error) {
|
|
67611
67698
|
this.logger.warn("failed to delete terminal histories for thread", {
|
|
@@ -67637,11 +67724,11 @@ var TerminalManagerRuntime = class extends EventEmitter {
|
|
|
67637
67724
|
}
|
|
67638
67725
|
historyPath(threadId, terminalId) {
|
|
67639
67726
|
const threadPart = toSafeThreadId(threadId);
|
|
67640
|
-
if (terminalId === "default") return
|
|
67641
|
-
return
|
|
67727
|
+
if (terminalId === "default") return nodePath.join(this.logsDir, `${threadPart}.log`);
|
|
67728
|
+
return nodePath.join(this.logsDir, `${threadPart}_${toSafeTerminalId(terminalId)}.log`);
|
|
67642
67729
|
}
|
|
67643
67730
|
legacyHistoryPath(threadId) {
|
|
67644
|
-
return
|
|
67731
|
+
return nodePath.join(this.logsDir, `${legacySafeThreadId(threadId)}.log`);
|
|
67645
67732
|
}
|
|
67646
67733
|
async runWithThreadLock(threadId, task) {
|
|
67647
67734
|
const previous = this.threadLocks.get(threadId) ?? Promise.resolve();
|
|
@@ -70617,8 +70704,8 @@ const NodePtyAdapterLive = effect(PtyAdapter, gen(function* () {
|
|
|
70617
70704
|
function makeServerProviderLayer() {
|
|
70618
70705
|
return gen(function* () {
|
|
70619
70706
|
const { stateDir } = yield* ServerConfig$1;
|
|
70620
|
-
const providerLogsDir =
|
|
70621
|
-
const providerEventLogPath =
|
|
70707
|
+
const providerLogsDir = nodePath.join(stateDir, "logs", "provider");
|
|
70708
|
+
const providerEventLogPath = nodePath.join(providerLogsDir, "events.log");
|
|
70622
70709
|
const nativeEventLogger = yield* makeEventNdjsonLogger(providerEventLogPath, { stream: "native" });
|
|
70623
70710
|
const canonicalEventLogger = yield* makeEventNdjsonLogger(providerEventLogPath, { stream: "canonical" });
|
|
70624
70711
|
const providerSessionDirectoryLayer = ProviderSessionDirectoryLive.pipe(provide$2(ProviderSessionRuntimeRepositoryLive));
|
|
@@ -72762,8 +72849,8 @@ function parseSchemaKeys(schemaRaw) {
|
|
|
72762
72849
|
async function readSchemaFromLocal() {
|
|
72763
72850
|
const candidates = [
|
|
72764
72851
|
process.env.CODEX_CONFIG_SCHEMA_PATH,
|
|
72765
|
-
|
|
72766
|
-
|
|
72852
|
+
nodePath.join(process.cwd(), "codex-rs", "core", "config.schema.json"),
|
|
72853
|
+
nodePath.join(process.cwd(), "node_modules", "@openai", "codex", "codex-rs", "core", "config.schema.json")
|
|
72767
72854
|
].filter((candidate) => Boolean(candidate));
|
|
72768
72855
|
for (const candidate of candidates) try {
|
|
72769
72856
|
return await readFile(candidate, "utf8");
|
|
@@ -72861,21 +72948,6 @@ const DEFAULT_CONFIG_TEMPLATE = [
|
|
|
72861
72948
|
"web_search = \"cached\"",
|
|
72862
72949
|
""
|
|
72863
72950
|
].join("\n");
|
|
72864
|
-
const SUPPORTED_REASONING_EFFORT = new Set([
|
|
72865
|
-
"minimal",
|
|
72866
|
-
"low",
|
|
72867
|
-
"medium",
|
|
72868
|
-
"high",
|
|
72869
|
-
"xhigh",
|
|
72870
|
-
"none"
|
|
72871
|
-
]);
|
|
72872
|
-
const CLI_SUPPORTED_REASONING_EFFORT = new Set([
|
|
72873
|
-
"minimal",
|
|
72874
|
-
"low",
|
|
72875
|
-
"medium",
|
|
72876
|
-
"high",
|
|
72877
|
-
"none"
|
|
72878
|
-
]);
|
|
72879
72951
|
const YOLO_PRESET = {
|
|
72880
72952
|
model: "gpt-5.3-codex",
|
|
72881
72953
|
reviewModel: "gpt-5.3-codex",
|
|
@@ -72901,10 +72973,10 @@ const YOLO_PRESET = {
|
|
|
72901
72973
|
//#endregion
|
|
72902
72974
|
//#region ../../packages/runtime-codex/src/codex/config-io.ts
|
|
72903
72975
|
function getConfigPath(options) {
|
|
72904
|
-
return
|
|
72976
|
+
return nodePath.join(resolveCodexHomeDir(options), "config.toml");
|
|
72905
72977
|
}
|
|
72906
72978
|
async function ensureConfigDirExists(filePath) {
|
|
72907
|
-
await mkdir(
|
|
72979
|
+
await mkdir(nodePath.dirname(filePath), { recursive: true });
|
|
72908
72980
|
}
|
|
72909
72981
|
function normalizeLineEndings(content) {
|
|
72910
72982
|
return content.replace(/\r\n/g, "\n");
|
|
@@ -73168,14 +73240,6 @@ function buildSettingsSummary(data) {
|
|
|
73168
73240
|
}
|
|
73169
73241
|
};
|
|
73170
73242
|
}
|
|
73171
|
-
function getUnsupportedReasoningEffort(data, allowed = SUPPORTED_REASONING_EFFORT) {
|
|
73172
|
-
if (!data || !Object.prototype.hasOwnProperty.call(data, "model_reasoning_effort")) return null;
|
|
73173
|
-
const raw = data["model_reasoning_effort"];
|
|
73174
|
-
if (typeof raw !== "string") return String(raw ?? "");
|
|
73175
|
-
const value = raw.trim();
|
|
73176
|
-
if (!value) return null;
|
|
73177
|
-
return allowed.has(value) ? null : value;
|
|
73178
|
-
}
|
|
73179
73243
|
function parseRmcpClientEnabled(data) {
|
|
73180
73244
|
const rawFeatures = data && typeof data["features"] === "object" && data["features"] !== null && !Array.isArray(data["features"]) ? data["features"] : null;
|
|
73181
73245
|
if (rawFeatures && typeof rawFeatures["rmcp_client"] === "boolean") return rawFeatures["rmcp_client"];
|
|
@@ -73479,21 +73543,6 @@ async function updateCodexConfigValues(updates, options) {
|
|
|
73479
73543
|
await writeConfigContent(serialized, options);
|
|
73480
73544
|
return buildSnapshot(serialized, true, options);
|
|
73481
73545
|
}
|
|
73482
|
-
async function sanitizeCodexConfigForCli(options) {
|
|
73483
|
-
const { content } = await readConfigContent(options);
|
|
73484
|
-
const parsed = tryParseToml(content);
|
|
73485
|
-
if (!parsed.data) throw new Error(parsed.error ?? "config.toml contains invalid TOML syntax.");
|
|
73486
|
-
if (!getUnsupportedReasoningEffort(parsed.data, CLI_SUPPORTED_REASONING_EFFORT)) return null;
|
|
73487
|
-
const sanitized = { ...parsed.data };
|
|
73488
|
-
delete sanitized["model_reasoning_effort"];
|
|
73489
|
-
await writeConfigContent(ensureTrailingNewline((0, import_toml.stringify)(sanitized)), options);
|
|
73490
|
-
let restored = false;
|
|
73491
|
-
return async () => {
|
|
73492
|
-
if (restored) return;
|
|
73493
|
-
restored = true;
|
|
73494
|
-
await writeConfigContent(ensureTrailingNewline(normalizeLineEndings(content)), options);
|
|
73495
|
-
};
|
|
73496
|
-
}
|
|
73497
73546
|
//#endregion
|
|
73498
73547
|
//#region ../../packages/contracts/src/profiles/identity.ts
|
|
73499
73548
|
function normalizeValue(value) {
|
|
@@ -73676,10 +73725,10 @@ function parseRateLimitSnapshotFromRpcMessage(message) {
|
|
|
73676
73725
|
}
|
|
73677
73726
|
async function fetchRateLimitsViaRpc(envOverride, options = {}) {
|
|
73678
73727
|
const binaryPath = options.codexPath ?? await requireCodexCli();
|
|
73679
|
-
const tempHome = await promises.mkdtemp(
|
|
73680
|
-
const tempAuthPath =
|
|
73728
|
+
const tempHome = await promises.mkdtemp(nodePath.join(nodeOs.tmpdir(), "codex-rpc-"));
|
|
73729
|
+
const tempAuthPath = nodePath.join(tempHome, "auth.json");
|
|
73681
73730
|
let initialSourceAuth = null;
|
|
73682
|
-
const sourceAuthPath = options.authPath ?? (envOverride?.CODEX_HOME ?
|
|
73731
|
+
const sourceAuthPath = options.authPath ?? (envOverride?.CODEX_HOME ? nodePath.join(envOverride.CODEX_HOME, "auth.json") : nodePath.join(envOverride?.HOME ?? process.env.HOME ?? process.env.USERPROFILE ?? nodeOs.homedir(), ".codex", "auth.json"));
|
|
73683
73732
|
try {
|
|
73684
73733
|
initialSourceAuth = await promises.readFile(sourceAuthPath, "utf8").catch(() => null);
|
|
73685
73734
|
if (!initialSourceAuth) return null;
|
|
@@ -73828,21 +73877,6 @@ var ProfileManager = class {
|
|
|
73828
73877
|
isNotFoundError(error) {
|
|
73829
73878
|
return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
|
|
73830
73879
|
}
|
|
73831
|
-
async readPreferredProfileName() {
|
|
73832
|
-
try {
|
|
73833
|
-
return await getLastProfileName();
|
|
73834
|
-
} catch (error) {
|
|
73835
|
-
logWarn("Failed to read preferred profile name:", error);
|
|
73836
|
-
return null;
|
|
73837
|
-
}
|
|
73838
|
-
}
|
|
73839
|
-
async persistPreferredProfileName(name) {
|
|
73840
|
-
try {
|
|
73841
|
-
await persistLastProfileName(name);
|
|
73842
|
-
} catch (error) {
|
|
73843
|
-
logWarn("Failed to persist preferred profile name:", error);
|
|
73844
|
-
}
|
|
73845
|
-
}
|
|
73846
73880
|
toStateRecord(record) {
|
|
73847
73881
|
return {
|
|
73848
73882
|
name: record.name,
|
|
@@ -73941,44 +73975,33 @@ var ProfileManager = class {
|
|
|
73941
73975
|
return null;
|
|
73942
73976
|
}
|
|
73943
73977
|
}
|
|
73944
|
-
|
|
73945
|
-
|
|
73978
|
+
emptyAuthSnapshot(fingerprint = null) {
|
|
73979
|
+
return {
|
|
73980
|
+
fingerprint,
|
|
73981
|
+
email: null,
|
|
73982
|
+
accountId: null,
|
|
73983
|
+
userId: null,
|
|
73984
|
+
chatgptUserId: null,
|
|
73985
|
+
chatgptAccountUserId: null,
|
|
73986
|
+
workspaceId: null
|
|
73987
|
+
};
|
|
73988
|
+
}
|
|
73989
|
+
async captureAuthSnapshotAtPath(authPath) {
|
|
73946
73990
|
let raw;
|
|
73947
73991
|
try {
|
|
73948
|
-
raw = await promises$1.readFile(
|
|
73992
|
+
raw = await promises$1.readFile(authPath, "utf8");
|
|
73949
73993
|
} catch (error) {
|
|
73950
|
-
if (this.isNotFoundError(error)) return
|
|
73951
|
-
fingerprint: null,
|
|
73952
|
-
email: null,
|
|
73953
|
-
accountId: null,
|
|
73954
|
-
userId: null,
|
|
73955
|
-
chatgptUserId: null,
|
|
73956
|
-
workspaceId: null
|
|
73957
|
-
};
|
|
73994
|
+
if (this.isNotFoundError(error)) return this.emptyAuthSnapshot();
|
|
73958
73995
|
const message = error instanceof Error ? error.message : "unknown error";
|
|
73959
|
-
const signature = typeof message === "string" ? `${message}:${
|
|
73996
|
+
const signature = typeof message === "string" ? `${message}:${authPath}` : authPath;
|
|
73960
73997
|
if (this.lastActiveAuthErrorSignature !== signature) {
|
|
73961
|
-
logWarn("Failed to snapshot
|
|
73998
|
+
logWarn("Failed to snapshot auth file:", error);
|
|
73962
73999
|
this.lastActiveAuthErrorSignature = signature;
|
|
73963
74000
|
}
|
|
73964
|
-
return
|
|
73965
|
-
fingerprint: null,
|
|
73966
|
-
email: null,
|
|
73967
|
-
accountId: null,
|
|
73968
|
-
userId: null,
|
|
73969
|
-
chatgptUserId: null,
|
|
73970
|
-
workspaceId: null
|
|
73971
|
-
};
|
|
74001
|
+
return this.emptyAuthSnapshot();
|
|
73972
74002
|
}
|
|
73973
74003
|
const trimmed = raw.trim();
|
|
73974
|
-
if (!trimmed) return
|
|
73975
|
-
fingerprint: null,
|
|
73976
|
-
email: null,
|
|
73977
|
-
accountId: null,
|
|
73978
|
-
userId: null,
|
|
73979
|
-
chatgptUserId: null,
|
|
73980
|
-
workspaceId: null
|
|
73981
|
-
};
|
|
74004
|
+
if (!trimmed) return this.emptyAuthSnapshot();
|
|
73982
74005
|
const fingerprint = createHash("sha256").update(trimmed).digest("hex");
|
|
73983
74006
|
try {
|
|
73984
74007
|
const parsed = JSON.parse(trimmed);
|
|
@@ -73991,19 +74014,25 @@ var ProfileManager = class {
|
|
|
73991
74014
|
accountId: this.getAccountIdFromData(normalized) ?? null,
|
|
73992
74015
|
userId: metadata?.userId ?? null,
|
|
73993
74016
|
chatgptUserId: metadata?.chatgptUserId ?? null,
|
|
74017
|
+
chatgptAccountUserId: metadata?.chatgptAccountUserId ?? null,
|
|
73994
74018
|
workspaceId: workspace.id ?? null
|
|
73995
74019
|
};
|
|
73996
74020
|
} catch {
|
|
73997
|
-
return
|
|
73998
|
-
fingerprint,
|
|
73999
|
-
email: null,
|
|
74000
|
-
accountId: null,
|
|
74001
|
-
userId: null,
|
|
74002
|
-
chatgptUserId: null,
|
|
74003
|
-
workspaceId: null
|
|
74004
|
-
};
|
|
74021
|
+
return this.emptyAuthSnapshot(fingerprint);
|
|
74005
74022
|
}
|
|
74006
74023
|
}
|
|
74024
|
+
async captureAuthSnapshot(authPath) {
|
|
74025
|
+
const targetPath = authPath ?? this.activeAuth;
|
|
74026
|
+
if (targetPath === this.activeAuth) return this.enqueueAuthSwap(async () => {
|
|
74027
|
+
await this.initialize();
|
|
74028
|
+
return this.captureAuthSnapshotAtPath(targetPath);
|
|
74029
|
+
});
|
|
74030
|
+
await this.initialize();
|
|
74031
|
+
return this.captureAuthSnapshotAtPath(targetPath);
|
|
74032
|
+
}
|
|
74033
|
+
async captureActiveAuthSnapshot() {
|
|
74034
|
+
return this.captureAuthSnapshot(this.activeAuth);
|
|
74035
|
+
}
|
|
74007
74036
|
/**
|
|
74008
74037
|
* Decode a JWT payload into an object without validating the signature.
|
|
74009
74038
|
*/
|
|
@@ -74264,7 +74293,7 @@ var ProfileManager = class {
|
|
|
74264
74293
|
}
|
|
74265
74294
|
async writeAtomic(filePath, contents) {
|
|
74266
74295
|
const tempPath = `${filePath}.tmp`;
|
|
74267
|
-
const dir = path
|
|
74296
|
+
const dir = path.dirname(filePath);
|
|
74268
74297
|
await promises$1.mkdir(dir, { recursive: true });
|
|
74269
74298
|
await promises$1.writeFile(tempPath, contents, "utf8");
|
|
74270
74299
|
try {
|
|
@@ -74319,6 +74348,99 @@ var ProfileManager = class {
|
|
|
74319
74348
|
metadata: record.metadata
|
|
74320
74349
|
}) ?? resolveFallbackAccountKey(record.name);
|
|
74321
74350
|
}
|
|
74351
|
+
describeActiveAuth(auth) {
|
|
74352
|
+
const normalized = this.normalizeProfileData(auth);
|
|
74353
|
+
if (typeof normalized.auth_method === "string") normalized.auth_method = normalized.auth_method.trim().toLowerCase();
|
|
74354
|
+
normalized.auth_method = normalized.auth_method ?? "codex-cli";
|
|
74355
|
+
const metadata = this.extractProfileMetadata(normalized);
|
|
74356
|
+
const workspace = this.resolveWorkspaceIdentity(normalized, metadata);
|
|
74357
|
+
const workspaceId = workspace.id || DEFAULT_WORKSPACE_ID;
|
|
74358
|
+
const workspaceName = workspace.name ?? null;
|
|
74359
|
+
normalized.workspace_id = normalized.workspace_id ?? workspaceId;
|
|
74360
|
+
if (workspaceName) normalized.workspace_name = normalized.workspace_name ?? workspaceName;
|
|
74361
|
+
const email = this.resolveProfileEmail(normalized, metadata) ?? null;
|
|
74362
|
+
if (email) normalized.email = email;
|
|
74363
|
+
const accountId = this.getAccountIdFromData(normalized) ?? null;
|
|
74364
|
+
return {
|
|
74365
|
+
normalized,
|
|
74366
|
+
metadata,
|
|
74367
|
+
workspaceId,
|
|
74368
|
+
workspaceName,
|
|
74369
|
+
email,
|
|
74370
|
+
accountId,
|
|
74371
|
+
identityKey: this.resolveProfileIdentityFromData(null, normalized, metadata),
|
|
74372
|
+
hasTokenPayload: Boolean(normalized.id_token || normalized.access_token || normalized.refresh_token || accountId || email)
|
|
74373
|
+
};
|
|
74374
|
+
}
|
|
74375
|
+
async findProfileForActiveAuth(identityKey, workspaceId) {
|
|
74376
|
+
return this.getProfileRowByIdentityAndWorkspace(identityKey, workspaceId, { preferAuthMethod: "codex-cli" });
|
|
74377
|
+
}
|
|
74378
|
+
async syncMatchedProfileFromActiveAuth(record, description) {
|
|
74379
|
+
if (!this.hasTokenChanges(record.data, description.normalized)) return;
|
|
74380
|
+
const { profile: merged, metadata } = this.mergeProfileRecords(record.data, description.normalized);
|
|
74381
|
+
delete merged.tokenAlert;
|
|
74382
|
+
await this.persistProfileRecord(record.name, merged, metadata);
|
|
74383
|
+
}
|
|
74384
|
+
async assertActiveAuthMatchesRecord(record, message) {
|
|
74385
|
+
const activeAuth = await this.readActiveAuthFile();
|
|
74386
|
+
if (!activeAuth) throw new Error("Active Codex auth is missing. Refresh profiles and try again.");
|
|
74387
|
+
const activeDescription = this.describeActiveAuth(activeAuth);
|
|
74388
|
+
const targetIdentity = this.resolveProfileIdentityFromRecord(record);
|
|
74389
|
+
const targetWorkspaceId = this.normalizeWorkspaceId(record.workspaceId ?? record.data.workspace_id);
|
|
74390
|
+
if (activeDescription.identityKey !== targetIdentity || this.normalizeWorkspaceId(activeDescription.workspaceId) !== targetWorkspaceId) throw new Error(message);
|
|
74391
|
+
}
|
|
74392
|
+
async writeActiveAuthForRecord(record) {
|
|
74393
|
+
const profileData = record.data;
|
|
74394
|
+
const targetIdentity = this.resolveProfileIdentityFromRecord(record);
|
|
74395
|
+
const targetWorkspaceId = this.normalizeWorkspaceId(record.workspaceId ?? profileData.workspace_id);
|
|
74396
|
+
const codexFormat = this.convertToCodexFormat(profileData);
|
|
74397
|
+
await this.writeAtomic(this.activeAuth, JSON.stringify(codexFormat, null, 2));
|
|
74398
|
+
const written = await this.readActiveAuthFile();
|
|
74399
|
+
if (!written) throw new Error("Codex auth file was not written.");
|
|
74400
|
+
const writtenDescription = this.describeActiveAuth(written);
|
|
74401
|
+
if (writtenDescription.identityKey !== targetIdentity) throw new Error("Codex auth verification failed after profile switch.");
|
|
74402
|
+
if (this.normalizeWorkspaceId(writtenDescription.workspaceId) !== targetWorkspaceId) throw new Error("Codex auth workspace verification failed after profile switch.");
|
|
74403
|
+
}
|
|
74404
|
+
async syncActiveAuthFromProfileIfCurrent(name) {
|
|
74405
|
+
const profileName = this.normalizeProfileName(name);
|
|
74406
|
+
const record = await this.getProfileRowByName(profileName);
|
|
74407
|
+
if (!record) return;
|
|
74408
|
+
const activeAuth = await this.readActiveAuthFile();
|
|
74409
|
+
if (!activeAuth) return;
|
|
74410
|
+
const activeDescription = this.describeActiveAuth(activeAuth);
|
|
74411
|
+
const targetIdentity = this.resolveProfileIdentityFromRecord(record);
|
|
74412
|
+
const targetWorkspaceId = this.normalizeWorkspaceId(record.workspaceId ?? record.data.workspace_id);
|
|
74413
|
+
if (activeDescription.identityKey !== targetIdentity || this.normalizeWorkspaceId(activeDescription.workspaceId) !== targetWorkspaceId) return;
|
|
74414
|
+
await this.writeActiveAuthForRecord(record);
|
|
74415
|
+
}
|
|
74416
|
+
async removeActiveAuthFiles() {
|
|
74417
|
+
await promises$1.rm(this.activeAuth, { force: true });
|
|
74418
|
+
await promises$1.rm(this.activeAuthBackup, { force: true });
|
|
74419
|
+
await promises$1.rm(`${this.activeAuth}.tmp`, { force: true });
|
|
74420
|
+
}
|
|
74421
|
+
resolveAutoImportedProfileName(email, existingRecords) {
|
|
74422
|
+
const base = (email ? email.split("@")[0] : "codex-account").trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "codex-account";
|
|
74423
|
+
const used = new Set(existingRecords.map((record) => record.name));
|
|
74424
|
+
let candidate = this.normalizeProfileName(base);
|
|
74425
|
+
let suffix = 2;
|
|
74426
|
+
while (used.has(candidate)) {
|
|
74427
|
+
candidate = this.normalizeProfileName(`${base}-${suffix}`);
|
|
74428
|
+
suffix += 1;
|
|
74429
|
+
}
|
|
74430
|
+
return candidate;
|
|
74431
|
+
}
|
|
74432
|
+
buildActiveCodexAuthStatus(state, description, options) {
|
|
74433
|
+
return {
|
|
74434
|
+
state,
|
|
74435
|
+
profileName: options?.profileName ?? null,
|
|
74436
|
+
email: description?.email ?? null,
|
|
74437
|
+
accountId: description?.accountId ?? null,
|
|
74438
|
+
workspaceId: description?.workspaceId ?? null,
|
|
74439
|
+
workspaceName: description?.workspaceName ?? null,
|
|
74440
|
+
importedProfileName: options?.importedProfileName ?? null,
|
|
74441
|
+
importBlockedReason: options?.importBlockedReason ?? null
|
|
74442
|
+
};
|
|
74443
|
+
}
|
|
74322
74444
|
resolveWorkspaceIdentity(data, metadata) {
|
|
74323
74445
|
const directId = "workspace_id" in data && typeof data.workspace_id === "string" ? data.workspace_id.trim() : void 0;
|
|
74324
74446
|
const directName = "workspace_name" in data && typeof data.workspace_name === "string" ? data.workspace_name.trim() : void 0;
|
|
@@ -74343,15 +74465,6 @@ var ProfileManager = class {
|
|
|
74343
74465
|
const normalized = this.normalizeProfileName(name);
|
|
74344
74466
|
return await this.readProfileRecord(normalized) ?? void 0;
|
|
74345
74467
|
}
|
|
74346
|
-
async getProfileRowByIdentityKey(identityKey, options) {
|
|
74347
|
-
const matches = (await this.listProfileRecords()).filter((record) => this.resolveProfileIdentityFromRecord(record) === identityKey);
|
|
74348
|
-
if (matches.length === 0) return;
|
|
74349
|
-
const mustMatch = typeof options?.authMethod === "string" ? options.authMethod.trim().toLowerCase() : null;
|
|
74350
|
-
if (mustMatch) return matches.find((record) => this.resolveAuthMethod(record.data) === mustMatch);
|
|
74351
|
-
const prefer = typeof options?.preferAuthMethod === "string" ? options.preferAuthMethod.trim().toLowerCase() : null;
|
|
74352
|
-
if (prefer) return matches.find((record) => this.resolveAuthMethod(record.data) === prefer) ?? matches[0];
|
|
74353
|
-
return matches[0];
|
|
74354
|
-
}
|
|
74355
74468
|
async getProfileRowByIdentityAndWorkspace(identityKey, workspaceId, options) {
|
|
74356
74469
|
const workspaceKey = workspaceId && workspaceId.trim().length > 0 ? workspaceId.trim() : DEFAULT_WORKSPACE_ID;
|
|
74357
74470
|
const matches = (await this.listProfileRecords()).filter((record) => {
|
|
@@ -74366,6 +74479,9 @@ var ProfileManager = class {
|
|
|
74366
74479
|
if (prefer) return matches.find((record) => this.resolveAuthMethod(record.data) === prefer) ?? matches[0];
|
|
74367
74480
|
return matches[0];
|
|
74368
74481
|
}
|
|
74482
|
+
normalizeWorkspaceId(value) {
|
|
74483
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : DEFAULT_WORKSPACE_ID;
|
|
74484
|
+
}
|
|
74369
74485
|
async persistProfileRecord(name, data, metadata, options) {
|
|
74370
74486
|
const resolvedName = this.normalizeProfileName(name);
|
|
74371
74487
|
const existingRecord = await this.readProfileRecord(resolvedName);
|
|
@@ -74413,6 +74529,23 @@ var ProfileManager = class {
|
|
|
74413
74529
|
metadata: row.metadata
|
|
74414
74530
|
});
|
|
74415
74531
|
}
|
|
74532
|
+
async deleteProfileRecordAndHome(record) {
|
|
74533
|
+
const profileName = this.normalizeProfileName(record.name);
|
|
74534
|
+
if (!record.accountId) {
|
|
74535
|
+
const dashboardState = await this.readProfileDashboardState();
|
|
74536
|
+
delete dashboardState.customGroupsByAccountKey[resolveFallbackAccountKey(profileName)];
|
|
74537
|
+
await this.writeProfileDashboardState(dashboardState);
|
|
74538
|
+
}
|
|
74539
|
+
try {
|
|
74540
|
+
await promises$1.rm(this.getProfileHomePath(profileName), {
|
|
74541
|
+
recursive: true,
|
|
74542
|
+
force: true
|
|
74543
|
+
});
|
|
74544
|
+
} catch (error) {
|
|
74545
|
+
if (!this.isNotFoundError(error)) logWarn(`Failed to remove profile home for '${profileName}':`, error);
|
|
74546
|
+
}
|
|
74547
|
+
await this.deleteProfileRecord(profileName);
|
|
74548
|
+
}
|
|
74416
74549
|
buildProfileFromData(name, data, fallback) {
|
|
74417
74550
|
const metadata = fallback?.metadata ?? this.extractProfileMetadata(data);
|
|
74418
74551
|
const workspace = this.resolveWorkspaceIdentity(data, metadata);
|
|
@@ -74464,6 +74597,12 @@ var ProfileManager = class {
|
|
|
74464
74597
|
}
|
|
74465
74598
|
};
|
|
74466
74599
|
if (normalizedLastRefresh) codexAuth.last_refresh = normalizedLastRefresh;
|
|
74600
|
+
const email = typeof data.email === "string" ? data.email.trim() : "";
|
|
74601
|
+
if (email) codexAuth.email = email;
|
|
74602
|
+
const workspaceId = typeof data.workspace_id === "string" ? data.workspace_id.trim() : "";
|
|
74603
|
+
if (workspaceId) codexAuth.workspace_id = workspaceId;
|
|
74604
|
+
const workspaceName = typeof data.workspace_name === "string" ? data.workspace_name.trim() : "";
|
|
74605
|
+
if (workspaceName) codexAuth.workspace_name = workspaceName;
|
|
74467
74606
|
return codexAuth;
|
|
74468
74607
|
}
|
|
74469
74608
|
/**
|
|
@@ -74482,7 +74621,12 @@ var ProfileManager = class {
|
|
|
74482
74621
|
const rootLastRefresh = typeof data.last_refresh === "string" ? data.last_refresh.trim() : "";
|
|
74483
74622
|
const normalizedLastRefresh = tokenLastRefresh || rootLastRefresh;
|
|
74484
74623
|
if (normalizedLastRefresh) profile.last_refresh = normalizedLastRefresh;
|
|
74485
|
-
|
|
74624
|
+
const email = typeof data.email === "string" ? data.email.trim() : "";
|
|
74625
|
+
if (email) profile.email = email;
|
|
74626
|
+
const workspaceId = typeof data.workspace_id === "string" ? data.workspace_id.trim() : "";
|
|
74627
|
+
if (workspaceId) profile.workspace_id = workspaceId;
|
|
74628
|
+
const workspaceName = typeof data.workspace_name === "string" ? data.workspace_name.trim() : "";
|
|
74629
|
+
if (workspaceName) profile.workspace_name = workspaceName;
|
|
74486
74630
|
return profile;
|
|
74487
74631
|
}
|
|
74488
74632
|
mergeProfileRecords(existing, incoming) {
|
|
@@ -74584,6 +74728,10 @@ var ProfileManager = class {
|
|
|
74584
74728
|
logWarn(`Skipped syncing tokens for profile '${profileName}' because Codex auth switched to a different OpenAI identity.`);
|
|
74585
74729
|
return;
|
|
74586
74730
|
}
|
|
74731
|
+
if (this.normalizeWorkspaceId(existing.workspace_id) !== this.normalizeWorkspaceId(normalized.workspace_id)) {
|
|
74732
|
+
logWarn(`Skipped syncing tokens for profile '${profileName}' because Codex auth switched to a different workspace.`);
|
|
74733
|
+
return;
|
|
74734
|
+
}
|
|
74587
74735
|
if (!this.hasTokenChanges(existing, normalized)) return;
|
|
74588
74736
|
const { profile: merged, metadata: mergedMetadata } = this.mergeProfileRecords(existing, normalized);
|
|
74589
74737
|
try {
|
|
@@ -74684,7 +74832,7 @@ var ProfileManager = class {
|
|
|
74684
74832
|
if (profileData.auth_method && profileData.auth_method !== "codex-cli") throw new Error("Unsupported auth method. Codex CLI profiles only.");
|
|
74685
74833
|
const codexFormat = this.convertToCodexFormat(profileData);
|
|
74686
74834
|
const profileHome = await this.prepareProfileHome(profileName, codexFormat);
|
|
74687
|
-
const authPath = path
|
|
74835
|
+
const authPath = path.join(profileHome, "auth.json");
|
|
74688
74836
|
const envOverrides = {
|
|
74689
74837
|
...process.env,
|
|
74690
74838
|
HOME: profileHome,
|
|
@@ -74738,7 +74886,7 @@ var ProfileManager = class {
|
|
|
74738
74886
|
const profileName = this.normalizeProfileName(name);
|
|
74739
74887
|
const record = await this.getProfileRowByName(profileName);
|
|
74740
74888
|
if (!record) throw new Error(`Profile '${profileName}' not found!`);
|
|
74741
|
-
const authPath = path
|
|
74889
|
+
const authPath = path.join(profileHome ?? this.getProfileHomePath(profileName), "auth.json");
|
|
74742
74890
|
let finalAuthContent = null;
|
|
74743
74891
|
try {
|
|
74744
74892
|
finalAuthContent = await promises$1.readFile(authPath, "utf8");
|
|
@@ -74747,79 +74895,70 @@ var ProfileManager = class {
|
|
|
74747
74895
|
}
|
|
74748
74896
|
if (finalAuthContent) {
|
|
74749
74897
|
await this.syncProfileTokensFromAuthContent(profileName, record.data, finalAuthContent);
|
|
74898
|
+
await this.syncActiveAuthFromProfileIfCurrent(profileName);
|
|
74750
74899
|
return;
|
|
74751
74900
|
}
|
|
74752
74901
|
await this.syncProfileTokensFromActiveAuth(profileName, record.data, authPath);
|
|
74902
|
+
await this.syncActiveAuthFromProfileIfCurrent(profileName);
|
|
74753
74903
|
}));
|
|
74754
74904
|
}
|
|
74755
74905
|
/**
|
|
74756
74906
|
* Create a new profile by running codex login
|
|
74757
74907
|
*/
|
|
74758
|
-
async
|
|
74759
|
-
|
|
74760
|
-
const profileName = this.normalizeProfileName(name);
|
|
74761
|
-
if (await this.getProfileRowByName(profileName)) throw new Error(`Profile '${profileName}' already exists!`);
|
|
74762
|
-
let authData;
|
|
74908
|
+
async readAuthFileForImport(authPath, actionLabel) {
|
|
74909
|
+
let authRaw;
|
|
74763
74910
|
try {
|
|
74764
|
-
|
|
74765
|
-
authData = JSON.parse(authRaw);
|
|
74911
|
+
authRaw = await promises$1.readFile(authPath, "utf8");
|
|
74766
74912
|
} catch (error) {
|
|
74767
|
-
|
|
74768
|
-
|
|
74913
|
+
if (this.isNotFoundError(error)) throw new Error(`Codex CLI did not produce an auth file. Complete the login before ${actionLabel}.`);
|
|
74914
|
+
logError(`Failed to read Codex auth file during ${actionLabel}:`, error);
|
|
74915
|
+
throw new Error("Failed to read Codex auth file. Complete the login and try again.");
|
|
74769
74916
|
}
|
|
74770
|
-
const normalizedProfile = this.normalizeProfileData(authData);
|
|
74771
|
-
normalizedProfile.created_at = normalizedProfile.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
74772
|
-
if (typeof normalizedProfile.auth_method === "string") normalizedProfile.auth_method = normalizedProfile.auth_method.trim().toLowerCase();
|
|
74773
|
-
normalizedProfile.auth_method = normalizedProfile.auth_method ?? "codex-cli";
|
|
74774
|
-
const metadata = this.extractProfileMetadata(normalizedProfile);
|
|
74775
|
-
const workspace = this.resolveWorkspaceIdentity(normalizedProfile, metadata);
|
|
74776
|
-
normalizedProfile.workspace_id = normalizedProfile.workspace_id ?? workspace.id ?? DEFAULT_WORKSPACE_ID;
|
|
74777
|
-
if (workspace.name) normalizedProfile.workspace_name = normalizedProfile.workspace_name ?? workspace.name;
|
|
74778
|
-
const resolvedEmail = this.resolveProfileEmail(normalizedProfile, metadata);
|
|
74779
|
-
if (resolvedEmail) normalizedProfile.email = resolvedEmail;
|
|
74780
74917
|
try {
|
|
74781
|
-
|
|
74782
|
-
const stored = await this.getProfileRowByName(profileName);
|
|
74783
|
-
return stored ? this.buildProfileFromRow(stored) : null;
|
|
74918
|
+
return JSON.parse(authRaw);
|
|
74784
74919
|
} catch (error) {
|
|
74785
|
-
logError(
|
|
74786
|
-
throw new Error("Failed to
|
|
74920
|
+
logError(`Failed to parse Codex auth file during ${actionLabel}:`, error);
|
|
74921
|
+
throw new Error("Failed to parse Codex auth file. Complete the login and try again.");
|
|
74787
74922
|
}
|
|
74788
74923
|
}
|
|
74789
|
-
|
|
74790
|
-
|
|
74791
|
-
|
|
74792
|
-
|
|
74793
|
-
await this.
|
|
74924
|
+
async persistNewProfileFromAuth(profileName, authData) {
|
|
74925
|
+
const description = this.describeActiveAuth(authData);
|
|
74926
|
+
const normalizedProfile = description.normalized;
|
|
74927
|
+
normalizedProfile.created_at = normalizedProfile.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
74928
|
+
await this.persistProfileRecord(profileName, normalizedProfile, description.metadata);
|
|
74929
|
+
const stored = await this.getProfileRowByName(profileName);
|
|
74930
|
+
return stored ? this.buildProfileFromRow(stored) : null;
|
|
74931
|
+
}
|
|
74932
|
+
async createProfile(name) {
|
|
74794
74933
|
const profileName = this.normalizeProfileName(name);
|
|
74795
|
-
|
|
74796
|
-
|
|
74797
|
-
|
|
74798
|
-
|
|
74799
|
-
|
|
74800
|
-
|
|
74801
|
-
|
|
74802
|
-
|
|
74803
|
-
|
|
74804
|
-
|
|
74805
|
-
|
|
74806
|
-
}
|
|
74934
|
+
return this.enqueueProfileOperation(profileName, () => this.enqueueAuthSwap(async () => {
|
|
74935
|
+
await this.initialize();
|
|
74936
|
+
if (await this.getProfileRowByName(profileName)) throw new Error(`Profile '${profileName}' already exists!`);
|
|
74937
|
+
try {
|
|
74938
|
+
const authData = await this.readAuthFileForImport(this.activeAuth, "profile creation");
|
|
74939
|
+
return await this.persistNewProfileFromAuth(profileName, authData);
|
|
74940
|
+
} catch (error) {
|
|
74941
|
+
logError("Error creating profile:", error);
|
|
74942
|
+
throw new Error("Failed to create profile. Make sure Codex CLI is installed and you are logged in.");
|
|
74943
|
+
}
|
|
74944
|
+
}));
|
|
74807
74945
|
}
|
|
74808
|
-
async
|
|
74809
|
-
await this.initialize();
|
|
74946
|
+
async createProfileFromAuthFile(name, authPath) {
|
|
74810
74947
|
const profileName = this.normalizeProfileName(name);
|
|
74811
|
-
|
|
74812
|
-
|
|
74948
|
+
return this.enqueueProfileOperation(profileName, async () => {
|
|
74949
|
+
await this.initialize();
|
|
74950
|
+
if (await this.getProfileRowByName(profileName)) throw new Error(`Profile '${profileName}' already exists!`);
|
|
74951
|
+
try {
|
|
74952
|
+
const authData = await this.readAuthFileForImport(authPath, "profile creation");
|
|
74953
|
+
return await this.persistNewProfileFromAuth(profileName, authData);
|
|
74954
|
+
} catch (error) {
|
|
74955
|
+
logError("Error creating profile:", error);
|
|
74956
|
+
throw new Error("Failed to create profile. Make sure Codex CLI is installed and you are logged in.");
|
|
74957
|
+
}
|
|
74958
|
+
});
|
|
74959
|
+
}
|
|
74960
|
+
async refreshProfileAuthWithData(profileName, record, activeAuth) {
|
|
74813
74961
|
const existing = record.data;
|
|
74814
|
-
let activeAuth;
|
|
74815
|
-
try {
|
|
74816
|
-
const authContent = await promises$1.readFile(this.activeAuth, "utf8");
|
|
74817
|
-
activeAuth = JSON.parse(authContent);
|
|
74818
|
-
} catch (error) {
|
|
74819
|
-
if (this.isNotFoundError(error)) throw new Error("Codex CLI did not produce an auth file. Complete the login before refreshing this profile.");
|
|
74820
|
-
logError("Failed to read Codex auth file during refresh:", error);
|
|
74821
|
-
throw new Error("Failed to read Codex auth file. Complete the login and try again.");
|
|
74822
|
-
}
|
|
74823
74962
|
const normalized = this.normalizeProfileData(activeAuth);
|
|
74824
74963
|
const existingMetadata = record.metadata ?? this.extractProfileMetadata(existing);
|
|
74825
74964
|
const currentWorkspace = this.resolveWorkspaceIdentity(existing, existingMetadata);
|
|
@@ -74852,15 +74991,72 @@ var ProfileManager = class {
|
|
|
74852
74991
|
return profile;
|
|
74853
74992
|
}
|
|
74854
74993
|
/**
|
|
74994
|
+
* Switch to a different profile
|
|
74995
|
+
*/
|
|
74996
|
+
async switchToProfile(name) {
|
|
74997
|
+
const profileName = this.normalizeProfileName(name);
|
|
74998
|
+
return this.enqueueProfileOperation(profileName, () => this.enqueueAuthSwap(async () => {
|
|
74999
|
+
await this.initialize();
|
|
75000
|
+
const record = await this.getProfileRowByName(profileName);
|
|
75001
|
+
if (!record) throw new Error(`Profile '${profileName}' not found!`);
|
|
75002
|
+
try {
|
|
75003
|
+
await this.writeActiveAuthForRecord(record);
|
|
75004
|
+
return true;
|
|
75005
|
+
} catch (error) {
|
|
75006
|
+
logError("Error switching profile:", error);
|
|
75007
|
+
throw new Error("Failed to switch profile");
|
|
75008
|
+
}
|
|
75009
|
+
}));
|
|
75010
|
+
}
|
|
75011
|
+
async refreshProfileAuth(name) {
|
|
75012
|
+
const profileName = this.normalizeProfileName(name);
|
|
75013
|
+
return this.enqueueProfileOperation(profileName, () => this.enqueueAuthSwap(async () => {
|
|
75014
|
+
await this.initialize();
|
|
75015
|
+
const record = await this.getProfileRowByName(profileName);
|
|
75016
|
+
if (!record) throw new Error(`Profile '${profileName}' not found!`);
|
|
75017
|
+
const activeAuth = await this.readAuthFileForImport(this.activeAuth, "profile refresh");
|
|
75018
|
+
return this.refreshProfileAuthWithData(profileName, record, activeAuth);
|
|
75019
|
+
}));
|
|
75020
|
+
}
|
|
75021
|
+
async refreshProfileAuthFromFile(name, authPath) {
|
|
75022
|
+
const profileName = this.normalizeProfileName(name);
|
|
75023
|
+
return this.enqueueProfileOperation(profileName, async () => {
|
|
75024
|
+
await this.initialize();
|
|
75025
|
+
const record = await this.getProfileRowByName(profileName);
|
|
75026
|
+
if (!record) throw new Error(`Profile '${profileName}' not found!`);
|
|
75027
|
+
const activeAuth = await this.readAuthFileForImport(authPath, "profile refresh");
|
|
75028
|
+
const profile = await this.refreshProfileAuthWithData(profileName, record, activeAuth);
|
|
75029
|
+
await this.enqueueAuthSwap(async () => {
|
|
75030
|
+
await this.syncActiveAuthFromProfileIfCurrent(profileName);
|
|
75031
|
+
});
|
|
75032
|
+
return profile;
|
|
75033
|
+
});
|
|
75034
|
+
}
|
|
75035
|
+
/**
|
|
74855
75036
|
* Execute an action with a profile's auth injected.
|
|
74856
75037
|
* Creates an isolated CODEX_HOME with the profile's auth/config, invokes the action
|
|
74857
75038
|
* with env overrides, then syncs any refreshed tokens back to the profile.
|
|
74858
75039
|
*/
|
|
74859
75040
|
async runWithProfileAuth(name, action) {
|
|
74860
|
-
return this.enqueueProfileOperation(name, () => this.enqueueAuthSwap(() =>
|
|
75041
|
+
return this.enqueueProfileOperation(name, () => this.enqueueAuthSwap(async () => {
|
|
75042
|
+
try {
|
|
75043
|
+
return await this.runWithPreparedProfileHome(name, action, { syncFromActiveAuthBeforeAction: true });
|
|
75044
|
+
} finally {
|
|
75045
|
+
await this.syncActiveAuthFromProfileIfCurrent(name);
|
|
75046
|
+
}
|
|
75047
|
+
}));
|
|
74861
75048
|
}
|
|
74862
75049
|
async readLiveRateLimits(name, options = {}) {
|
|
74863
|
-
|
|
75050
|
+
const profileName = this.normalizeProfileName(name);
|
|
75051
|
+
try {
|
|
75052
|
+
return await this.enqueueProfileOperation(profileName, () => this.runWithPreparedProfileHome(profileName, (env) => fetchRateLimitsViaRpc(env, { codexPath: options.codexPath }), { syncFromActiveAuthBeforeAction: false }));
|
|
75053
|
+
} finally {
|
|
75054
|
+
await this.enqueueAuthSwap(async () => {
|
|
75055
|
+
await this.syncActiveAuthFromProfileIfCurrent(profileName);
|
|
75056
|
+
}).catch((error) => {
|
|
75057
|
+
logWarn(`Failed to sync active auth after rate-limit probe for '${profileName}':`, error);
|
|
75058
|
+
});
|
|
75059
|
+
}
|
|
74864
75060
|
}
|
|
74865
75061
|
/**
|
|
74866
75062
|
* Rename a profile
|
|
@@ -74872,8 +75068,6 @@ var ProfileManager = class {
|
|
|
74872
75068
|
const existing = await this.getProfileRowByName(sourceName);
|
|
74873
75069
|
if (!existing) throw new Error(`Profile '${sourceName}' not found!`);
|
|
74874
75070
|
if (await this.getProfileRowByName(targetName)) throw new Error(`Profile '${targetName}' already exists!`);
|
|
74875
|
-
const preferred = await this.readPreferredProfileName();
|
|
74876
|
-
if (preferred && preferred === sourceName) await this.persistPreferredProfileName(targetName);
|
|
74877
75071
|
const oldHome = this.getProfileHomePath(sourceName);
|
|
74878
75072
|
const newHome = this.getProfileHomePath(targetName);
|
|
74879
75073
|
try {
|
|
@@ -74930,24 +75124,37 @@ var ProfileManager = class {
|
|
|
74930
75124
|
const profileName = this.normalizeProfileName(name);
|
|
74931
75125
|
const record = await this.getProfileRowByName(profileName);
|
|
74932
75126
|
if (!record) throw new Error(`Profile '${profileName}' not found!`);
|
|
74933
|
-
|
|
74934
|
-
const dashboardState = await this.readProfileDashboardState();
|
|
74935
|
-
delete dashboardState.customGroupsByAccountKey[resolveFallbackAccountKey(profileName)];
|
|
74936
|
-
await this.writeProfileDashboardState(dashboardState);
|
|
74937
|
-
}
|
|
74938
|
-
const preferred = await this.readPreferredProfileName();
|
|
74939
|
-
if (preferred && preferred === profileName) await this.persistPreferredProfileName(null);
|
|
74940
|
-
try {
|
|
74941
|
-
await promises$1.rm(this.getProfileHomePath(profileName), {
|
|
74942
|
-
recursive: true,
|
|
74943
|
-
force: true
|
|
74944
|
-
});
|
|
74945
|
-
} catch (error) {
|
|
74946
|
-
if (!this.isNotFoundError(error)) logWarn(`Failed to remove profile home for '${profileName}':`, error);
|
|
74947
|
-
}
|
|
74948
|
-
await this.deleteProfileRecord(profileName);
|
|
75127
|
+
await this.deleteProfileRecordAndHome(record);
|
|
74949
75128
|
return true;
|
|
74950
75129
|
}
|
|
75130
|
+
async deleteActiveProfileAndSwitch(name, replacementName) {
|
|
75131
|
+
const profileName = this.normalizeProfileName(name);
|
|
75132
|
+
const nextProfileName = this.normalizeProfileName(replacementName);
|
|
75133
|
+
if (profileName === nextProfileName) throw new Error("Replacement profile must be different from the deleted profile.");
|
|
75134
|
+
return this.enqueueAuthSwap(async () => {
|
|
75135
|
+
await this.initialize();
|
|
75136
|
+
const record = await this.getProfileRowByName(profileName);
|
|
75137
|
+
if (!record) throw new Error(`Profile '${profileName}' not found!`);
|
|
75138
|
+
const replacement = await this.getProfileRowByName(nextProfileName);
|
|
75139
|
+
if (!replacement) throw new Error(`Profile '${nextProfileName}' not found!`);
|
|
75140
|
+
await this.assertActiveAuthMatchesRecord(record, "Active Codex login changed before deletion. Refresh profiles and try again.");
|
|
75141
|
+
await this.writeActiveAuthForRecord(replacement);
|
|
75142
|
+
await this.deleteProfileRecordAndHome(record);
|
|
75143
|
+
return true;
|
|
75144
|
+
});
|
|
75145
|
+
}
|
|
75146
|
+
async deleteActiveProfileAndAuth(name) {
|
|
75147
|
+
const profileName = this.normalizeProfileName(name);
|
|
75148
|
+
return this.enqueueAuthSwap(async () => {
|
|
75149
|
+
await this.initialize();
|
|
75150
|
+
const record = await this.getProfileRowByName(profileName);
|
|
75151
|
+
if (!record) throw new Error(`Profile '${profileName}' not found!`);
|
|
75152
|
+
await this.assertActiveAuthMatchesRecord(record, "Active Codex login changed before deletion. Refresh profiles and try again.");
|
|
75153
|
+
await this.removeActiveAuthFiles();
|
|
75154
|
+
await this.deleteProfileRecordAndHome(record);
|
|
75155
|
+
return true;
|
|
75156
|
+
});
|
|
75157
|
+
}
|
|
74951
75158
|
/**
|
|
74952
75159
|
* Delete every profile that does not appear in the allow-list.
|
|
74953
75160
|
* Used to enforce plan limits when local storage was tampered with.
|
|
@@ -74977,67 +75184,46 @@ var ProfileManager = class {
|
|
|
74977
75184
|
} catch (error) {
|
|
74978
75185
|
if (!this.isNotFoundError(error)) logWarn(`Failed to remove profile '${name}' during plan enforcement:`, error);
|
|
74979
75186
|
}
|
|
74980
|
-
const preferred = await this.readPreferredProfileName();
|
|
74981
|
-
if (preferred && removed.includes(preferred)) await this.persistPreferredProfileName(null);
|
|
74982
75187
|
return removed;
|
|
74983
75188
|
}
|
|
75189
|
+
async getActiveCodexAuthStatus(options = {}) {
|
|
75190
|
+
return this.enqueueAuthSwap(async () => {
|
|
75191
|
+
await this.initialize();
|
|
75192
|
+
const activeAuth = await this.readActiveAuthFile();
|
|
75193
|
+
if (!activeAuth) return this.buildActiveCodexAuthStatus("missing");
|
|
75194
|
+
const description = this.describeActiveAuth(activeAuth);
|
|
75195
|
+
if (!description.hasTokenPayload || !description.identityKey) return this.buildActiveCodexAuthStatus("unknown", description);
|
|
75196
|
+
const matching = await this.findProfileForActiveAuth(description.identityKey, description.workspaceId);
|
|
75197
|
+
if (matching) {
|
|
75198
|
+
const normalized = this.normalizeProfileName(matching.name);
|
|
75199
|
+
await this.syncMatchedProfileFromActiveAuth(matching, description);
|
|
75200
|
+
return this.buildActiveCodexAuthStatus("matched", description, { profileName: normalized });
|
|
75201
|
+
}
|
|
75202
|
+
if (!options.autoImport) return this.buildActiveCodexAuthStatus("unmanaged", description);
|
|
75203
|
+
if (options.canAutoImport === false) return this.buildActiveCodexAuthStatus("unmanaged", description, { importBlockedReason: options.importBlockedReason ?? "Profile limit reached. Upgrade to CodexUse Pro or remove a profile before importing this Codex login." });
|
|
75204
|
+
const records = await this.listProfileRecords();
|
|
75205
|
+
const profileName = this.resolveAutoImportedProfileName(description.email, records);
|
|
75206
|
+
const profileData = {
|
|
75207
|
+
...description.normalized,
|
|
75208
|
+
auth_method: "codex-cli",
|
|
75209
|
+
created_at: description.normalized.created_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
75210
|
+
};
|
|
75211
|
+
await this.persistProfileRecord(profileName, profileData, description.metadata, { displayName: description.email ?? profileName });
|
|
75212
|
+
return this.buildActiveCodexAuthStatus("matched", description, {
|
|
75213
|
+
profileName,
|
|
75214
|
+
importedProfileName: profileName
|
|
75215
|
+
});
|
|
75216
|
+
});
|
|
75217
|
+
}
|
|
74984
75218
|
/**
|
|
74985
|
-
* Check if the
|
|
75219
|
+
* Check if the real Codex auth file matches a saved profile.
|
|
74986
75220
|
*/
|
|
74987
75221
|
async getCurrentProfile() {
|
|
74988
|
-
await this.
|
|
74989
|
-
|
|
74990
|
-
|
|
74991
|
-
|
|
74992
|
-
|
|
74993
|
-
name: normalizedPreferred,
|
|
74994
|
-
trusted: true
|
|
74995
|
-
};
|
|
74996
|
-
} catch {}
|
|
74997
|
-
const activeAuth = await this.readActiveAuthFile();
|
|
74998
|
-
if (activeAuth) {
|
|
74999
|
-
const normalizedAuth = this.normalizeProfileData(activeAuth);
|
|
75000
|
-
const authMetadata = this.extractProfileMetadata(normalizedAuth);
|
|
75001
|
-
const workspace = this.resolveWorkspaceIdentity(normalizedAuth, authMetadata);
|
|
75002
|
-
const activeIdentityKey = this.resolveProfileIdentityFromData(null, normalizedAuth, authMetadata);
|
|
75003
|
-
if (activeIdentityKey) {
|
|
75004
|
-
const scoped = await this.getProfileRowByIdentityAndWorkspace(activeIdentityKey, workspace.id ?? DEFAULT_WORKSPACE_ID, { preferAuthMethod: "codex-cli" });
|
|
75005
|
-
if (scoped) {
|
|
75006
|
-
const normalized = this.normalizeProfileName(scoped.name);
|
|
75007
|
-
await this.persistPreferredProfileName(normalized);
|
|
75008
|
-
return {
|
|
75009
|
-
name: normalized,
|
|
75010
|
-
trusted: true
|
|
75011
|
-
};
|
|
75012
|
-
}
|
|
75013
|
-
const matching = await this.getProfileRowByIdentityKey(activeIdentityKey, { preferAuthMethod: "codex-cli" });
|
|
75014
|
-
if (matching) {
|
|
75015
|
-
const normalized = this.normalizeProfileName(matching.name);
|
|
75016
|
-
await this.persistPreferredProfileName(normalized);
|
|
75017
|
-
return {
|
|
75018
|
-
name: normalized,
|
|
75019
|
-
trusted: true
|
|
75020
|
-
};
|
|
75021
|
-
}
|
|
75022
|
-
}
|
|
75023
|
-
}
|
|
75024
|
-
const preferred = await this.readPreferredProfileName();
|
|
75025
|
-
if (preferred) {
|
|
75026
|
-
try {
|
|
75027
|
-
const normalized = this.normalizeProfileName(preferred);
|
|
75028
|
-
if (await this.getProfileRowByName(normalized)) return {
|
|
75029
|
-
name: normalized,
|
|
75030
|
-
trusted: true
|
|
75031
|
-
};
|
|
75032
|
-
} catch {
|
|
75033
|
-
await this.persistPreferredProfileName(null);
|
|
75034
|
-
return {
|
|
75035
|
-
name: null,
|
|
75036
|
-
trusted: false
|
|
75037
|
-
};
|
|
75038
|
-
}
|
|
75039
|
-
await this.persistPreferredProfileName(null);
|
|
75040
|
-
}
|
|
75222
|
+
const status = await this.getActiveCodexAuthStatus({ autoImport: false });
|
|
75223
|
+
if (status.state === "matched" && status.profileName) return {
|
|
75224
|
+
name: status.profileName,
|
|
75225
|
+
trusted: true
|
|
75226
|
+
};
|
|
75041
75227
|
return {
|
|
75042
75228
|
name: null,
|
|
75043
75229
|
trusted: false
|
|
@@ -75129,13 +75315,6 @@ var ProfileManager = class {
|
|
|
75129
75315
|
if (!this.isNotFoundError(error)) logWarn(`Failed to remove profile '${name}' during cloud sync replace:`, error);
|
|
75130
75316
|
}
|
|
75131
75317
|
}
|
|
75132
|
-
const preferred = await this.readPreferredProfileName();
|
|
75133
|
-
if (preferred) try {
|
|
75134
|
-
const normalizedPreferred = this.normalizeProfileName(preferred);
|
|
75135
|
-
if (!normalized.has(normalizedPreferred)) await this.persistPreferredProfileName(null);
|
|
75136
|
-
} catch {
|
|
75137
|
-
await this.persistPreferredProfileName(null);
|
|
75138
|
-
}
|
|
75139
75318
|
return {
|
|
75140
75319
|
imported: normalized.size,
|
|
75141
75320
|
removed
|
|
@@ -75159,6 +75338,58 @@ async function loadCachedRateLimitSnapshots(keys) {
|
|
|
75159
75338
|
return results;
|
|
75160
75339
|
}
|
|
75161
75340
|
//#endregion
|
|
75341
|
+
//#region ../../packages/runtime-profiles/src/profiles/profile-visibility.ts
|
|
75342
|
+
function resolveLicenseVisibleProfiles(args) {
|
|
75343
|
+
const { profiles, license, currentProfile } = args;
|
|
75344
|
+
const licenseWithCounts = licenseService.applyProfileCount(license, profiles.length);
|
|
75345
|
+
const enforceProfileLimit = shouldEnforceProfileLimit(licenseWithCounts);
|
|
75346
|
+
const visibleProfiles = enforceProfileLimit ? resolveVisibleProfiles(profiles, licenseWithCounts, currentProfile) : profiles;
|
|
75347
|
+
return {
|
|
75348
|
+
allProfiles: profiles,
|
|
75349
|
+
profiles: licenseWithCounts.isPro ? profiles : visibleProfiles,
|
|
75350
|
+
license,
|
|
75351
|
+
licenseWithCounts,
|
|
75352
|
+
enforceProfileLimit
|
|
75353
|
+
};
|
|
75354
|
+
}
|
|
75355
|
+
async function loadLicenseVisibleProfiles(profileManager, options = {}) {
|
|
75356
|
+
await profileManager.initialize();
|
|
75357
|
+
const license = options.freshLicense ? await licenseService.getStatus() : await licenseService.getCachedStatus();
|
|
75358
|
+
return resolveLicenseVisibleProfiles({
|
|
75359
|
+
profiles: await profileManager.listProfiles(),
|
|
75360
|
+
license,
|
|
75361
|
+
currentProfile: options.currentProfile !== void 0 ? options.currentProfile : (await profileManager.getCurrentProfile()).name
|
|
75362
|
+
});
|
|
75363
|
+
}
|
|
75364
|
+
function resolveVisibleProfiles(profiles, license, currentProfile) {
|
|
75365
|
+
if (license.isPro) return profiles;
|
|
75366
|
+
const limit = typeof license.profileLimit === "number" && license.profileLimit >= 0 ? license.profileLimit : 2;
|
|
75367
|
+
if (profiles.length <= limit) return profiles;
|
|
75368
|
+
const normalizedCurrent = currentProfile ?? null;
|
|
75369
|
+
return profiles.slice().sort((a, b) => compareProfilesForLimit(a, b, normalizedCurrent)).slice(0, limit);
|
|
75370
|
+
}
|
|
75371
|
+
function compareProfilesForLimit(a, b, currentProfile) {
|
|
75372
|
+
if (currentProfile) {
|
|
75373
|
+
if (a.name === currentProfile && b.name !== currentProfile) return -1;
|
|
75374
|
+
if (b.name === currentProfile && a.name !== currentProfile) return 1;
|
|
75375
|
+
}
|
|
75376
|
+
if (Boolean(a.isValid) !== Boolean(b.isValid)) return a.isValid ? -1 : 1;
|
|
75377
|
+
const aTimestamp = parseTimestamp(a.createdAt ?? null);
|
|
75378
|
+
const bTimestamp = parseTimestamp(b.createdAt ?? null);
|
|
75379
|
+
if (aTimestamp !== null && bTimestamp !== null) {
|
|
75380
|
+
if (aTimestamp === bTimestamp) return a.name.localeCompare(b.name);
|
|
75381
|
+
return bTimestamp - aTimestamp;
|
|
75382
|
+
}
|
|
75383
|
+
if (aTimestamp !== null) return -1;
|
|
75384
|
+
if (bTimestamp !== null) return 1;
|
|
75385
|
+
return a.name.localeCompare(b.name);
|
|
75386
|
+
}
|
|
75387
|
+
function parseTimestamp(value) {
|
|
75388
|
+
if (typeof value !== "string" || value.trim() === "") return null;
|
|
75389
|
+
const parsed = Date.parse(value);
|
|
75390
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
75391
|
+
}
|
|
75392
|
+
//#endregion
|
|
75162
75393
|
//#region ../../packages/runtime-profiles/src/profiles/rate-limit-probe.ts
|
|
75163
75394
|
const MIN_REFRESH_INTERVAL_MS = 3 * 6e4;
|
|
75164
75395
|
const DEFAULT_REFRESH_INTERVAL_MS = 3 * 6e4;
|
|
@@ -75552,7 +75783,8 @@ async function executeAccountRefresh(accountId, state, options = {}) {
|
|
|
75552
75783
|
async function bootstrapInitialRefresh() {
|
|
75553
75784
|
try {
|
|
75554
75785
|
await ensureProfileManagerReady();
|
|
75555
|
-
|
|
75786
|
+
const { profiles } = await loadLicenseVisibleProfiles(sharedProfileManager);
|
|
75787
|
+
await refreshRateLimitTelemetry(sharedProfileManager, profiles, /* @__PURE__ */ new Map(), { enqueueAll: true });
|
|
75556
75788
|
emitRefreshState();
|
|
75557
75789
|
} catch (error) {
|
|
75558
75790
|
logError("Failed to bootstrap rate-limit refresh queue:", error);
|
|
@@ -75589,66 +75821,67 @@ function profileKey(profile) {
|
|
|
75589
75821
|
}
|
|
75590
75822
|
/**
|
|
75591
75823
|
* Loads profiles and related telemetry for the dashboard.
|
|
75592
|
-
* When
|
|
75593
|
-
*
|
|
75824
|
+
* When a free user has more saved profiles than allowed, extra profiles are hidden,
|
|
75825
|
+
* not deleted.
|
|
75594
75826
|
*/
|
|
75595
75827
|
async function loadProfilesViewData(options = {}) {
|
|
75596
75828
|
const backgroundRefresh = options.backgroundRefresh !== false;
|
|
75597
75829
|
await profileManager.initialize();
|
|
75598
75830
|
const license = await licenseService.getCachedStatus();
|
|
75599
75831
|
if (backgroundRefresh) licenseService.refreshStatusInBackground();
|
|
75600
|
-
const
|
|
75832
|
+
const initialVisible = resolveLicenseVisibleProfiles({
|
|
75833
|
+
profiles: await profileManager.listProfiles(),
|
|
75834
|
+
license,
|
|
75835
|
+
currentProfile: null
|
|
75836
|
+
});
|
|
75837
|
+
const initialLicenseWithCounts = initialVisible.licenseWithCounts;
|
|
75838
|
+
const canAutoImportActiveAuth = !initialVisible.enforceProfileLimit || initialLicenseWithCounts.profilesRemaining === null || initialLicenseWithCounts.profilesRemaining > 0;
|
|
75839
|
+
const activeCodexAuth = await profileManager.getActiveCodexAuthStatus({
|
|
75840
|
+
autoImport: options.autoImportActiveAuth === true,
|
|
75841
|
+
canAutoImport: canAutoImportActiveAuth
|
|
75842
|
+
});
|
|
75843
|
+
const currentProfile = activeCodexAuth.state === "matched" && activeCodexAuth.profileName ? activeCodexAuth.profileName : null;
|
|
75844
|
+
const currentProfileTrusted = Boolean(currentProfile);
|
|
75601
75845
|
const profiles = await profileManager.listProfiles();
|
|
75602
75846
|
const customGroupsByAccountKey = await profileManager.getCustomGroupsByAccountKey();
|
|
75603
|
-
const
|
|
75604
|
-
|
|
75605
|
-
|
|
75606
|
-
|
|
75607
|
-
|
|
75608
|
-
|
|
75609
|
-
|
|
75610
|
-
|
|
75611
|
-
|
|
75612
|
-
|
|
75613
|
-
|
|
75614
|
-
|
|
75615
|
-
|
|
75616
|
-
|
|
75617
|
-
|
|
75618
|
-
|
|
75619
|
-
|
|
75620
|
-
|
|
75621
|
-
|
|
75622
|
-
|
|
75623
|
-
|
|
75624
|
-
|
|
75625
|
-
|
|
75626
|
-
|
|
75627
|
-
|
|
75628
|
-
|
|
75629
|
-
|
|
75630
|
-
profileName: issue.profileName,
|
|
75631
|
-
message: issue.message,
|
|
75632
|
-
code: issue.code,
|
|
75633
|
-
observedAt: issue.observedAt ? new Date(issue.observedAt).toISOString() : void 0
|
|
75634
|
-
}))
|
|
75635
|
-
};
|
|
75636
|
-
const snapshotMap = resolvedSnapshots;
|
|
75637
|
-
enrichedProfiles = effectiveProfiles.map((profile) => ({
|
|
75638
|
-
...profile,
|
|
75639
|
-
rateLimit: snapshotMap.get(profileKey(profile)) ?? null
|
|
75640
|
-
}));
|
|
75641
|
-
} else enrichedProfiles = effectiveProfiles.map((profile) => ({
|
|
75642
|
-
...profile,
|
|
75643
|
-
rateLimit: null
|
|
75644
|
-
}));
|
|
75847
|
+
const visible = resolveLicenseVisibleProfiles({
|
|
75848
|
+
profiles,
|
|
75849
|
+
license,
|
|
75850
|
+
currentProfile
|
|
75851
|
+
});
|
|
75852
|
+
const effectiveProfiles = visible.profiles;
|
|
75853
|
+
const rateLimitEligible = effectiveProfiles.filter((profile) => profile.isValid && !profile.tokenStatus?.requiresUserAction);
|
|
75854
|
+
const resolvedSnapshots = await resolveRateLimitSnapshots(rateLimitEligible.map((profile) => profileKey(profile)));
|
|
75855
|
+
if (backgroundRefresh) {
|
|
75856
|
+
ensureRateLimitRefresher();
|
|
75857
|
+
await refreshRateLimitTelemetry(profileManager, rateLimitEligible, resolvedSnapshots);
|
|
75858
|
+
}
|
|
75859
|
+
const refreshState = getRateLimitRefreshState();
|
|
75860
|
+
const refreshStatus = {
|
|
75861
|
+
pendingAccountIds: refreshState.queuedAccountIds.slice(),
|
|
75862
|
+
inFlightAccountIds: refreshState.inFlightAccountIds.slice(),
|
|
75863
|
+
cooldownAccountIds: refreshState.cooldownAccountIds.slice(),
|
|
75864
|
+
lastRefreshStartedAt: refreshState.lastRefreshStartedAt ? new Date(refreshState.lastRefreshStartedAt).toISOString() : void 0,
|
|
75865
|
+
lastRefreshCompletedAt: refreshState.lastRefreshCompletedAt ? new Date(refreshState.lastRefreshCompletedAt).toISOString() : void 0,
|
|
75866
|
+
errors: (refreshState.errors ?? []).map((issue) => ({
|
|
75867
|
+
accountId: issue.accountId,
|
|
75868
|
+
profileName: issue.profileName,
|
|
75869
|
+
message: issue.message,
|
|
75870
|
+
code: issue.code,
|
|
75871
|
+
observedAt: issue.observedAt ? new Date(issue.observedAt).toISOString() : void 0
|
|
75872
|
+
}))
|
|
75873
|
+
};
|
|
75645
75874
|
return {
|
|
75646
|
-
profiles:
|
|
75875
|
+
profiles: effectiveProfiles.map((profile) => ({
|
|
75876
|
+
...profile,
|
|
75877
|
+
rateLimit: resolvedSnapshots.get(profileKey(profile)) ?? null
|
|
75878
|
+
})),
|
|
75647
75879
|
currentProfile,
|
|
75648
75880
|
currentProfileTrusted,
|
|
75649
75881
|
refreshStatus,
|
|
75650
|
-
license: licenseWithCounts,
|
|
75651
|
-
customGroupsByAccountKey
|
|
75882
|
+
license: visible.licenseWithCounts,
|
|
75883
|
+
customGroupsByAccountKey,
|
|
75884
|
+
activeCodexAuth
|
|
75652
75885
|
};
|
|
75653
75886
|
}
|
|
75654
75887
|
async function resolveRateLimitSnapshots(accountIds) {
|
|
@@ -75656,34 +75889,6 @@ async function resolveRateLimitSnapshots(accountIds) {
|
|
|
75656
75889
|
if (uniqueAccountIds.length === 0) return /* @__PURE__ */ new Map();
|
|
75657
75890
|
return await loadCachedRateLimitSnapshots(uniqueAccountIds);
|
|
75658
75891
|
}
|
|
75659
|
-
function resolveVisibleProfiles(profiles, license, currentProfile) {
|
|
75660
|
-
if (license.isPro) return profiles;
|
|
75661
|
-
const limit = typeof license.profileLimit === "number" && license.profileLimit >= 0 ? license.profileLimit : 2;
|
|
75662
|
-
if (profiles.length <= limit) return profiles;
|
|
75663
|
-
const normalizedCurrent = currentProfile ?? null;
|
|
75664
|
-
return profiles.slice().sort((a, b) => compareProfilesForLimit(a, b, normalizedCurrent)).slice(0, limit);
|
|
75665
|
-
}
|
|
75666
|
-
function compareProfilesForLimit(a, b, currentProfile) {
|
|
75667
|
-
if (currentProfile) {
|
|
75668
|
-
if (a.name === currentProfile && b.name !== currentProfile) return -1;
|
|
75669
|
-
if (b.name === currentProfile && a.name !== currentProfile) return 1;
|
|
75670
|
-
}
|
|
75671
|
-
if (Boolean(a.isValid) !== Boolean(b.isValid)) return a.isValid ? -1 : 1;
|
|
75672
|
-
const aTimestamp = parseTimestamp(a.createdAt ?? null);
|
|
75673
|
-
const bTimestamp = parseTimestamp(b.createdAt ?? null);
|
|
75674
|
-
if (aTimestamp !== null && bTimestamp !== null) {
|
|
75675
|
-
if (aTimestamp === bTimestamp) return a.name.localeCompare(b.name);
|
|
75676
|
-
return bTimestamp - aTimestamp;
|
|
75677
|
-
}
|
|
75678
|
-
if (aTimestamp !== null) return -1;
|
|
75679
|
-
if (bTimestamp !== null) return 1;
|
|
75680
|
-
return a.name.localeCompare(b.name);
|
|
75681
|
-
}
|
|
75682
|
-
function parseTimestamp(value) {
|
|
75683
|
-
if (typeof value !== "string" || value.trim() === "") return null;
|
|
75684
|
-
const parsed = Date.parse(value);
|
|
75685
|
-
return Number.isNaN(parsed) ? null : parsed;
|
|
75686
|
-
}
|
|
75687
75892
|
//#endregion
|
|
75688
75893
|
//#region src/account-pool/service.ts
|
|
75689
75894
|
const ACCOUNT_POOL_DOCUMENT = "desktop.account-pool.v2";
|
|
@@ -75854,7 +76059,7 @@ function sessionTtlMs(affinityKind) {
|
|
|
75854
76059
|
}
|
|
75855
76060
|
}
|
|
75856
76061
|
function toStoreDbPath(stateDir) {
|
|
75857
|
-
return
|
|
76062
|
+
return nodePath.join(stateDir, "state.sqlite");
|
|
75858
76063
|
}
|
|
75859
76064
|
function createDefaultStore() {
|
|
75860
76065
|
return {
|
|
@@ -77726,15 +77931,19 @@ var LocalAccountPool = class LocalAccountPool {
|
|
|
77726
77931
|
return map;
|
|
77727
77932
|
}
|
|
77728
77933
|
async resolveProfiles(settings, store) {
|
|
77729
|
-
const [profilesView, autoRollSettings] = await Promise.all([loadProfilesViewData({
|
|
77934
|
+
const [profilesView, autoRollSettings] = await Promise.all([loadProfilesViewData({
|
|
77935
|
+
backgroundRefresh: false,
|
|
77936
|
+
autoImportActiveAuth: false
|
|
77937
|
+
}), getStoredAutoRollSettings()]);
|
|
77730
77938
|
const selectedProfileNames = settings.accountPoolProfilePool.length > 0 ? settings.accountPoolProfilePool.slice() : profilesView.profiles.map((profile) => profile.name);
|
|
77731
77939
|
const selectedSet = new Set(selectedProfileNames);
|
|
77732
|
-
const
|
|
77940
|
+
const switchRemainingThreshold = autoRollSettings?.switchRemainingThreshold ?? 5;
|
|
77733
77941
|
const now = Date.now();
|
|
77734
77942
|
const activeSessionCounts = this.buildActiveSessionCountMap(store);
|
|
77735
77943
|
const profileViews = profilesView.profiles.map((profile) => {
|
|
77736
77944
|
const selected = selectedSet.has(profile.name);
|
|
77737
77945
|
const usedPercent = maxRateLimitUsedPercent(profile);
|
|
77946
|
+
const remainingPercent = typeof usedPercent === "number" ? Math.max(0, 100 - usedPercent) : null;
|
|
77738
77947
|
const profileState = store.profilesByName[profile.name];
|
|
77739
77948
|
const cooldownUntilMs = profileState?.cooldownUntil ? Date.parse(profileState.cooldownUntil) : NaN;
|
|
77740
77949
|
let reason = null;
|
|
@@ -77742,7 +77951,7 @@ var LocalAccountPool = class LocalAccountPool {
|
|
|
77742
77951
|
else if (!profile.isValid) reason = "Profile auth is invalid.";
|
|
77743
77952
|
else if (profile.tokenStatus?.requiresUserAction) reason = profile.tokenStatus.reason ?? "Profile requires re-authentication.";
|
|
77744
77953
|
else if (Number.isFinite(cooldownUntilMs) && cooldownUntilMs > now) reason = `Cooling down until ${new Date(cooldownUntilMs).toLocaleTimeString()}.`;
|
|
77745
|
-
else if (
|
|
77954
|
+
else if (remainingPercent !== null && remainingPercent <= switchRemainingThreshold) reason = `Rate limit remaining is ${Math.round(remainingPercent)}%.`;
|
|
77746
77955
|
return {
|
|
77747
77956
|
name: profile.name,
|
|
77748
77957
|
displayName: profile.displayName ?? null,
|
|
@@ -78213,14 +78422,14 @@ function extractIconHref(source) {
|
|
|
78213
78422
|
}
|
|
78214
78423
|
function resolveIconHref(projectCwd, href) {
|
|
78215
78424
|
const clean = href.replace(/^\//, "");
|
|
78216
|
-
return [
|
|
78425
|
+
return [nodePath.join(projectCwd, "public", clean), nodePath.join(projectCwd, clean)];
|
|
78217
78426
|
}
|
|
78218
78427
|
function isPathWithinProject(projectCwd, candidatePath) {
|
|
78219
|
-
const relative =
|
|
78220
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
78428
|
+
const relative = nodePath.relative(nodePath.resolve(projectCwd), nodePath.resolve(candidatePath));
|
|
78429
|
+
return relative === "" || !relative.startsWith("..") && !nodePath.isAbsolute(relative);
|
|
78221
78430
|
}
|
|
78222
78431
|
function serveFaviconFile(filePath, res) {
|
|
78223
|
-
const contentType = FAVICON_MIME_TYPES[
|
|
78432
|
+
const contentType = FAVICON_MIME_TYPES[nodePath.extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
78224
78433
|
fs.readFile(filePath, (readErr, data) => {
|
|
78225
78434
|
if (readErr) {
|
|
78226
78435
|
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
@@ -78272,7 +78481,7 @@ function tryHandleProjectFaviconRequest(url, res) {
|
|
|
78272
78481
|
serveFallbackFavicon(res);
|
|
78273
78482
|
return;
|
|
78274
78483
|
}
|
|
78275
|
-
const sourceFile =
|
|
78484
|
+
const sourceFile = nodePath.join(projectCwd, ICON_SOURCE_FILES[index]);
|
|
78276
78485
|
fs.readFile(sourceFile, "utf8", (err, content) => {
|
|
78277
78486
|
if (err) {
|
|
78278
78487
|
trySourceFiles(index + 1);
|
|
@@ -78291,7 +78500,7 @@ function tryHandleProjectFaviconRequest(url, res) {
|
|
|
78291
78500
|
trySourceFiles(0);
|
|
78292
78501
|
return;
|
|
78293
78502
|
}
|
|
78294
|
-
const candidate =
|
|
78503
|
+
const candidate = nodePath.join(projectCwd, FAVICON_CANDIDATES[index]);
|
|
78295
78504
|
if (!isPathWithinProject(projectCwd, candidate)) {
|
|
78296
78505
|
tryCandidates(index + 1);
|
|
78297
78506
|
return;
|
|
@@ -78458,18 +78667,18 @@ const REGISTRY_TIMEOUT_MS = 1e4;
|
|
|
78458
78667
|
let freshnessCache = null;
|
|
78459
78668
|
function buildEnv() {
|
|
78460
78669
|
const env = { ...process.env };
|
|
78461
|
-
const homeDir = env.HOME ?? env.USERPROFILE ??
|
|
78670
|
+
const homeDir = env.HOME ?? env.USERPROFILE ?? nodeOs.homedir();
|
|
78462
78671
|
const extra = [
|
|
78463
78672
|
"/opt/homebrew/bin",
|
|
78464
78673
|
"/usr/local/bin",
|
|
78465
|
-
|
|
78466
|
-
|
|
78467
|
-
|
|
78468
|
-
|
|
78469
|
-
|
|
78674
|
+
nodePath.join(homeDir, ".local", "bin"),
|
|
78675
|
+
nodePath.join(homeDir, ".fnm", "aliases", "default", "bin"),
|
|
78676
|
+
nodePath.join(homeDir, ".fnm", "current", "bin"),
|
|
78677
|
+
nodePath.join(homeDir, ".volta", "bin"),
|
|
78678
|
+
nodePath.join(homeDir, ".asdf", "shims")
|
|
78470
78679
|
];
|
|
78471
|
-
const segments = [...(env.PATH ?? "").split(
|
|
78472
|
-
env.PATH = Array.from(new Set(segments)).join(
|
|
78680
|
+
const segments = [...(env.PATH ?? "").split(nodePath.delimiter).filter(Boolean), ...extra];
|
|
78681
|
+
env.PATH = Array.from(new Set(segments)).join(nodePath.delimiter);
|
|
78473
78682
|
return env;
|
|
78474
78683
|
}
|
|
78475
78684
|
function runCommand$2(command, args, env) {
|
|
@@ -78481,7 +78690,7 @@ function runCommand$2(command, args, env) {
|
|
|
78481
78690
|
function resolveRunnableCommand(command, env, shellInfo) {
|
|
78482
78691
|
const resolved = resolveCommandPath(command, env, shellInfo)?.trim();
|
|
78483
78692
|
if (!resolved) return command;
|
|
78484
|
-
if (resolved.includes(
|
|
78693
|
+
if (resolved.includes(nodePath.sep) || resolved.includes("/")) return resolved;
|
|
78485
78694
|
return command;
|
|
78486
78695
|
}
|
|
78487
78696
|
function resolveShell() {
|
|
@@ -78811,15 +79020,15 @@ function isMissingPathError(error) {
|
|
|
78811
79020
|
return error.code === "ENOENT";
|
|
78812
79021
|
}
|
|
78813
79022
|
function resolveHomeDir() {
|
|
78814
|
-
const home = process.env.HOME || process.env.USERPROFILE ||
|
|
79023
|
+
const home = process.env.HOME || process.env.USERPROFILE || nodeOs.homedir();
|
|
78815
79024
|
if (!home) throw new Error("HOME is not set.");
|
|
78816
79025
|
return home;
|
|
78817
79026
|
}
|
|
78818
79027
|
function resolveCodexDir() {
|
|
78819
|
-
return
|
|
79028
|
+
return nodePath.join(resolveHomeDir(), ".codex");
|
|
78820
79029
|
}
|
|
78821
79030
|
function resolveLegacyPath(...segments) {
|
|
78822
|
-
return
|
|
79031
|
+
return nodePath.join(resolveCodexDir(), ...segments);
|
|
78823
79032
|
}
|
|
78824
79033
|
function pickAutoRoll(raw) {
|
|
78825
79034
|
if (!raw) return null;
|
|
@@ -78888,9 +79097,13 @@ function mergeLegacyLocalStoragePatch(payload) {
|
|
|
78888
79097
|
if (autoRoll) {
|
|
78889
79098
|
patch.autoRoll = {
|
|
78890
79099
|
enabled: autoRoll.enabled,
|
|
78891
|
-
|
|
78892
|
-
|
|
78893
|
-
restartOfficialCodexOnAutoRoll: autoRoll.restartOfficialCodexOnAutoRoll
|
|
79100
|
+
rearmRemainingThreshold: autoRoll.rearmRemainingThreshold,
|
|
79101
|
+
switchRemainingThreshold: autoRoll.switchRemainingThreshold,
|
|
79102
|
+
restartOfficialCodexOnAutoRoll: autoRoll.restartOfficialCodexOnAutoRoll,
|
|
79103
|
+
launchOfficialCodexWhenClosedOnAutoRoll: autoRoll.launchOfficialCodexWhenClosedOnAutoRoll,
|
|
79104
|
+
priorityOrder: autoRoll.priorityOrder,
|
|
79105
|
+
lowRemainingNotificationEnabled: autoRoll.lowRemainingNotificationEnabled,
|
|
79106
|
+
lowRemainingNotificationThreshold: autoRoll.lowRemainingNotificationThreshold
|
|
78894
79107
|
};
|
|
78895
79108
|
markSkippedIfPresent("codex:auto-roll-settings", true);
|
|
78896
79109
|
} else markSkippedIfPresent("codex:auto-roll-settings", false);
|
|
@@ -78909,8 +79122,8 @@ async function removeIfExists(target) {
|
|
|
78909
79122
|
async function cleanupLegacyCanonicalSources() {
|
|
78910
79123
|
const homeDir = resolveHomeDir();
|
|
78911
79124
|
await removeIfExists(resolveLegacyAppStatePath());
|
|
78912
|
-
await removeIfExists(
|
|
78913
|
-
await removeIfExists(
|
|
79125
|
+
await removeIfExists(nodePath.join(getUserDataDir(), LEGACY_APP_SETTINGS_PARITY_FILE));
|
|
79126
|
+
await removeIfExists(nodePath.join(homeDir, SQLITE_STORAGE_DIR));
|
|
78914
79127
|
await removeIfExists(resolveLegacyPath(LEGACY_SETTINGS_FILE));
|
|
78915
79128
|
await removeIfExists(resolveLegacyPath(LEGACY_SETTINGS_BACKUP_FILE));
|
|
78916
79129
|
await removeIfExists(resolveLegacyPath(LEGACY_SYNC_STATE_FILE));
|
|
@@ -78921,7 +79134,7 @@ async function cleanupLegacyCanonicalSources() {
|
|
|
78921
79134
|
const profileHomes = await promises.readdir(profileHomesRoot, { withFileTypes: true });
|
|
78922
79135
|
for (const profileHome of profileHomes) {
|
|
78923
79136
|
if (!profileHome.isDirectory()) continue;
|
|
78924
|
-
const profileDir =
|
|
79137
|
+
const profileDir = nodePath.join(profileHomesRoot, profileHome.name);
|
|
78925
79138
|
let files = [];
|
|
78926
79139
|
try {
|
|
78927
79140
|
files = await promises.readdir(profileDir);
|
|
@@ -78930,7 +79143,7 @@ async function cleanupLegacyCanonicalSources() {
|
|
|
78930
79143
|
}
|
|
78931
79144
|
for (const file of files) {
|
|
78932
79145
|
if (!file.startsWith("profile.json")) continue;
|
|
78933
|
-
await removeIfExists(
|
|
79146
|
+
await removeIfExists(nodePath.join(profileDir, file));
|
|
78934
79147
|
}
|
|
78935
79148
|
}
|
|
78936
79149
|
} catch (error) {
|
|
@@ -78944,7 +79157,7 @@ async function cleanupLegacyCanonicalSources() {
|
|
|
78944
79157
|
const skillDirs = await promises.readdir(skillsRoot, { withFileTypes: true });
|
|
78945
79158
|
for (const entry of skillDirs) {
|
|
78946
79159
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
78947
|
-
await removeIfExists(
|
|
79160
|
+
await removeIfExists(nodePath.join(skillsRoot, entry.name, LEGACY_SKILL_MANIFEST));
|
|
78948
79161
|
}
|
|
78949
79162
|
} catch (error) {
|
|
78950
79163
|
if (!isMissingPathError(error)) logWarn("Failed cleaning legacy skills metadata:", {
|
|
@@ -79025,19 +79238,19 @@ async function importLegacyLocalStorageOnce(payload) {
|
|
|
79025
79238
|
//#region ../../packages/runtime-integrations/src/agents/service.ts
|
|
79026
79239
|
const AGENTS_FILENAME = "AGENTS.md";
|
|
79027
79240
|
function resolveGlobalPath(options) {
|
|
79028
|
-
return
|
|
79241
|
+
return nodePath.join(resolveCodexHomeDir(options), AGENTS_FILENAME);
|
|
79029
79242
|
}
|
|
79030
79243
|
function getCodexDir(options) {
|
|
79031
79244
|
return resolveCodexHomeDir(options);
|
|
79032
79245
|
}
|
|
79033
79246
|
function isPathSafe(normalizedPath, options) {
|
|
79034
|
-
if (
|
|
79247
|
+
if (nodePath.basename(normalizedPath) !== AGENTS_FILENAME) return false;
|
|
79035
79248
|
const codexDir = getCodexDir(options);
|
|
79036
79249
|
const homeDir = resolveHomeDir$1();
|
|
79037
|
-
if (normalizedPath ===
|
|
79038
|
-
const parentDir =
|
|
79039
|
-
if (!parentDir.startsWith(homeDir +
|
|
79040
|
-
if (parentDir.startsWith(codexDir +
|
|
79250
|
+
if (normalizedPath === nodePath.join(codexDir, AGENTS_FILENAME)) return true;
|
|
79251
|
+
const parentDir = nodePath.dirname(normalizedPath);
|
|
79252
|
+
if (!parentDir.startsWith(homeDir + nodePath.sep) && parentDir !== homeDir) return false;
|
|
79253
|
+
if (parentDir.startsWith(codexDir + nodePath.sep) || parentDir === codexDir) return false;
|
|
79041
79254
|
return true;
|
|
79042
79255
|
}
|
|
79043
79256
|
async function statFile(target) {
|
|
@@ -79060,7 +79273,7 @@ async function readFileContent(target) {
|
|
|
79060
79273
|
}
|
|
79061
79274
|
}
|
|
79062
79275
|
async function ensureDirForFile(target) {
|
|
79063
|
-
const dir =
|
|
79276
|
+
const dir = nodePath.dirname(target);
|
|
79064
79277
|
await promises.mkdir(dir, { recursive: true });
|
|
79065
79278
|
}
|
|
79066
79279
|
async function saveFileContent(target, content) {
|
|
@@ -79075,7 +79288,7 @@ function normalizeProjectPath(candidate) {
|
|
|
79075
79288
|
if (!candidate || typeof candidate !== "string") return null;
|
|
79076
79289
|
const trimmed = candidate.trim();
|
|
79077
79290
|
if (!trimmed) return null;
|
|
79078
|
-
return
|
|
79291
|
+
return nodePath.resolve(trimmed);
|
|
79079
79292
|
}
|
|
79080
79293
|
async function buildDescriptor(params) {
|
|
79081
79294
|
const stats = await statFile(params.path);
|
|
@@ -79101,7 +79314,7 @@ async function readPreferredContent(entries) {
|
|
|
79101
79314
|
async function getAgentsSnapshot(projectPath, options) {
|
|
79102
79315
|
const normalizedProject = normalizeProjectPath(projectPath);
|
|
79103
79316
|
const globalPath = resolveGlobalPath(options);
|
|
79104
|
-
const projectPathFile = normalizedProject ?
|
|
79317
|
+
const projectPathFile = normalizedProject ? nodePath.join(normalizedProject, AGENTS_FILENAME) : null;
|
|
79105
79318
|
const globalEntry = await buildDescriptor({
|
|
79106
79319
|
path: globalPath,
|
|
79107
79320
|
type: "global",
|
|
@@ -79123,7 +79336,7 @@ async function getAgentsSnapshot(projectPath, options) {
|
|
|
79123
79336
|
};
|
|
79124
79337
|
}
|
|
79125
79338
|
async function readAgentsFile(target, options) {
|
|
79126
|
-
const normalized =
|
|
79339
|
+
const normalized = nodePath.resolve(target);
|
|
79127
79340
|
if (!isPathSafe(normalized, options)) throw new Error("Access denied: path outside allowed directories");
|
|
79128
79341
|
const content = await readFileContent(normalized);
|
|
79129
79342
|
return {
|
|
@@ -79133,7 +79346,7 @@ async function readAgentsFile(target, options) {
|
|
|
79133
79346
|
};
|
|
79134
79347
|
}
|
|
79135
79348
|
async function saveAgentsFile(target, content, options) {
|
|
79136
|
-
const normalized =
|
|
79349
|
+
const normalized = nodePath.resolve(target);
|
|
79137
79350
|
if (!isPathSafe(normalized, options)) throw new Error("Access denied: path outside allowed directories");
|
|
79138
79351
|
await saveFileContent(normalized, content ?? "");
|
|
79139
79352
|
return readAgentsFile(normalized, options);
|
|
@@ -79333,7 +79546,7 @@ function formatMegabytes(bytes) {
|
|
|
79333
79546
|
return (bytes / MB_DIVISOR).toFixed(2);
|
|
79334
79547
|
}
|
|
79335
79548
|
function resolveSyncBackupsDir() {
|
|
79336
|
-
return
|
|
79549
|
+
return nodePath.join(getUserDataDir(), SYNC_BACKUP_DIR);
|
|
79337
79550
|
}
|
|
79338
79551
|
function toSafeIsoForFileName(iso) {
|
|
79339
79552
|
return iso.replace(/:/g, "-");
|
|
@@ -79341,14 +79554,14 @@ function toSafeIsoForFileName(iso) {
|
|
|
79341
79554
|
async function pruneOldPrePullBackups(backupsDir) {
|
|
79342
79555
|
const files = (await promises.readdir(backupsDir, { withFileTypes: true })).filter((entry) => entry.isFile() && entry.name.startsWith(PRE_PULL_BACKUP_PREFIX) && entry.name.endsWith(PRE_PULL_BACKUP_SUFFIX)).map((entry) => entry.name).sort((a, b) => b.localeCompare(a));
|
|
79343
79556
|
if (files.length <= PRE_PULL_BACKUP_KEEP_COUNT) return;
|
|
79344
|
-
for (const stale of files.slice(PRE_PULL_BACKUP_KEEP_COUNT)) await promises.rm(
|
|
79557
|
+
for (const stale of files.slice(PRE_PULL_BACKUP_KEEP_COUNT)) await promises.rm(nodePath.join(backupsDir, stale), { force: true });
|
|
79345
79558
|
}
|
|
79346
79559
|
async function createPrePullBackup(profileManager) {
|
|
79347
79560
|
const snapshot = await buildLocalSnapshot(profileManager, { enforcePushGuards: false });
|
|
79348
79561
|
const backupsDir = resolveSyncBackupsDir();
|
|
79349
79562
|
await promises.mkdir(backupsDir, { recursive: true });
|
|
79350
79563
|
const timestamp = toSafeIsoForFileName((/* @__PURE__ */ new Date()).toISOString());
|
|
79351
|
-
const backupPath =
|
|
79564
|
+
const backupPath = nodePath.join(backupsDir, `${PRE_PULL_BACKUP_PREFIX}${timestamp}${PRE_PULL_BACKUP_SUFFIX}`);
|
|
79352
79565
|
await promises.writeFile(backupPath, `${JSON.stringify(snapshot, null, 2)}\n`, "utf8");
|
|
79353
79566
|
await pruneOldPrePullBackups(backupsDir);
|
|
79354
79567
|
logInfo("[cloud-sync] created pre-pull backup", { backupPath });
|
|
@@ -79582,17 +79795,17 @@ function buildRuntimeEnv(codexHomePath) {
|
|
|
79582
79795
|
...process.env,
|
|
79583
79796
|
...codexHomePath?.trim() ? { CODEX_HOME: codexHomePath.trim() } : {}
|
|
79584
79797
|
};
|
|
79585
|
-
const homeDir = env.HOME ?? env.USERPROFILE ??
|
|
79798
|
+
const homeDir = env.HOME ?? env.USERPROFILE ?? nodeOs.homedir();
|
|
79586
79799
|
const extraPaths = [
|
|
79587
79800
|
"/opt/homebrew/bin",
|
|
79588
79801
|
"/usr/local/bin",
|
|
79589
|
-
|
|
79590
|
-
|
|
79591
|
-
|
|
79592
|
-
|
|
79593
|
-
|
|
79802
|
+
nodePath.join(homeDir, ".local", "bin"),
|
|
79803
|
+
nodePath.join(homeDir, ".fnm", "aliases", "default", "bin"),
|
|
79804
|
+
nodePath.join(homeDir, ".fnm", "current", "bin"),
|
|
79805
|
+
nodePath.join(homeDir, ".volta", "bin"),
|
|
79806
|
+
nodePath.join(homeDir, ".asdf", "shims")
|
|
79594
79807
|
];
|
|
79595
|
-
env.PATH = Array.from(new Set([...(env.PATH ?? "").split(
|
|
79808
|
+
env.PATH = Array.from(new Set([...(env.PATH ?? "").split(nodePath.delimiter), ...extraPaths].filter(Boolean))).join(nodePath.delimiter);
|
|
79596
79809
|
return env;
|
|
79597
79810
|
}
|
|
79598
79811
|
function createTimeoutError(command, args) {
|
|
@@ -79986,6 +80199,7 @@ const AUTO_ROLL_ATTEMPT_TTL_MS = 3e4;
|
|
|
79986
80199
|
const EMPTY_USAGE_SUMMARY = {
|
|
79987
80200
|
windowKey: null,
|
|
79988
80201
|
usagePercent: 0,
|
|
80202
|
+
remainingPercent: 100,
|
|
79989
80203
|
hasUsage: false,
|
|
79990
80204
|
resetsInSeconds: null,
|
|
79991
80205
|
windowMinutes: null
|
|
@@ -80018,12 +80232,14 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
80018
80232
|
const rawUsedPercent = finiteNumberOrNull(window.usedPercent);
|
|
80019
80233
|
const hasUsage = rawUsedPercent !== null;
|
|
80020
80234
|
const usagePercent = hasUsage ? Math.max(0, Math.min(100, rawUsedPercent)) : 0;
|
|
80235
|
+
const remainingPercent = 100 - usagePercent;
|
|
80021
80236
|
const resetsInSeconds = resolveResetsInSeconds(window);
|
|
80022
80237
|
const windowMinutes = finiteNumberOrNull(window.windowMinutes);
|
|
80023
80238
|
if (!summary.windowKey) {
|
|
80024
80239
|
summary = {
|
|
80025
80240
|
windowKey: entry.key,
|
|
80026
80241
|
usagePercent,
|
|
80242
|
+
remainingPercent,
|
|
80027
80243
|
hasUsage,
|
|
80028
80244
|
resetsInSeconds,
|
|
80029
80245
|
windowMinutes
|
|
@@ -80034,6 +80250,7 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
80034
80250
|
summary = {
|
|
80035
80251
|
windowKey: entry.key,
|
|
80036
80252
|
usagePercent,
|
|
80253
|
+
remainingPercent,
|
|
80037
80254
|
hasUsage,
|
|
80038
80255
|
resetsInSeconds,
|
|
80039
80256
|
windowMinutes
|
|
@@ -80044,6 +80261,7 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
80044
80261
|
summary = {
|
|
80045
80262
|
windowKey: entry.key,
|
|
80046
80263
|
usagePercent,
|
|
80264
|
+
remainingPercent,
|
|
80047
80265
|
hasUsage,
|
|
80048
80266
|
resetsInSeconds,
|
|
80049
80267
|
windowMinutes
|
|
@@ -80054,6 +80272,7 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
80054
80272
|
if (resetsInSeconds !== null && resetsInSeconds < summary.resetsInSeconds) summary = {
|
|
80055
80273
|
windowKey: entry.key,
|
|
80056
80274
|
usagePercent,
|
|
80275
|
+
remainingPercent,
|
|
80057
80276
|
hasUsage,
|
|
80058
80277
|
resetsInSeconds,
|
|
80059
80278
|
windowMinutes
|
|
@@ -80063,6 +80282,7 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
80063
80282
|
if (!hasUsage && !summary.hasUsage && summary.resetsInSeconds === null && resetsInSeconds !== null) summary = {
|
|
80064
80283
|
windowKey: entry.key,
|
|
80065
80284
|
usagePercent,
|
|
80285
|
+
remainingPercent,
|
|
80066
80286
|
hasUsage,
|
|
80067
80287
|
resetsInSeconds,
|
|
80068
80288
|
windowMinutes
|
|
@@ -80070,26 +80290,42 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
80070
80290
|
}
|
|
80071
80291
|
return summary;
|
|
80072
80292
|
}
|
|
80073
|
-
function
|
|
80074
|
-
|
|
80293
|
+
function buildPriorityRanks(priorityOrder) {
|
|
80294
|
+
const ranks = /* @__PURE__ */ new Map();
|
|
80295
|
+
for (const [index, key] of priorityOrder.entries()) {
|
|
80296
|
+
const normalized = typeof key === "string" ? key.trim() : "";
|
|
80297
|
+
if (!normalized || ranks.has(normalized)) continue;
|
|
80298
|
+
ranks.set(normalized, index);
|
|
80299
|
+
}
|
|
80300
|
+
return ranks;
|
|
80301
|
+
}
|
|
80302
|
+
function compareAutoRollCandidates(a, b, priorityRanks) {
|
|
80303
|
+
const aRank = priorityRanks.get(resolveProfileIdentityKeyFromProfile(a.profile));
|
|
80304
|
+
const bRank = priorityRanks.get(resolveProfileIdentityKeyFromProfile(b.profile));
|
|
80305
|
+
if (aRank !== void 0 || bRank !== void 0) {
|
|
80306
|
+
if (aRank === void 0) return 1;
|
|
80307
|
+
if (bRank === void 0) return -1;
|
|
80308
|
+
if (aRank !== bRank) return aRank - bRank;
|
|
80309
|
+
}
|
|
80310
|
+
if (a.usageSummary.remainingPercent !== b.usageSummary.remainingPercent) return b.usageSummary.remainingPercent - a.usageSummary.remainingPercent;
|
|
80075
80311
|
const aReset = a.usageSummary.resetsInSeconds ?? Number.POSITIVE_INFINITY;
|
|
80076
80312
|
const bReset = b.usageSummary.resetsInSeconds ?? Number.POSITIVE_INFINITY;
|
|
80077
80313
|
if (aReset !== bReset) return aReset - bReset;
|
|
80078
80314
|
return a.profile.name.localeCompare(b.profile.name);
|
|
80079
80315
|
}
|
|
80080
|
-
function computeAutoRollCandidate(profiles, usageSummaries, currentProfileName, currentSummary,
|
|
80081
|
-
const
|
|
80316
|
+
function computeAutoRollCandidate(profiles, usageSummaries, currentProfileName, currentSummary, switchRemainingThreshold, priorityOrder = [], blockedProfileNames = /* @__PURE__ */ new Set()) {
|
|
80317
|
+
const priorityRanks = buildPriorityRanks(priorityOrder);
|
|
80318
|
+
const candidates = profiles.filter((profile) => profile.name !== currentProfileName && !blockedProfileNames.has(profile.name) && profile.isValid && !profile.tokenStatus?.requiresUserAction).map((profile) => ({
|
|
80082
80319
|
profile,
|
|
80083
80320
|
usageSummary: usageSummaries.get(profile.name) ?? summarizeRateLimitSnapshot(profile.rateLimit ?? null)
|
|
80084
80321
|
}));
|
|
80085
80322
|
if (candidates.length === 0) return null;
|
|
80086
|
-
let selected = candidates.filter((candidate) => candidate.usageSummary.hasUsage && candidate.usageSummary.
|
|
80087
|
-
if (!selected) selected = candidates.filter((candidate) => candidate.usageSummary.hasUsage).sort(compareAutoRollCandidates)[0] ?? null;
|
|
80088
|
-
if (!selected) selected = candidates.filter((candidate) => !candidate.usageSummary.hasUsage).sort(compareAutoRollCandidates)[0] ?? null;
|
|
80323
|
+
let selected = candidates.filter((candidate) => candidate.usageSummary.hasUsage && candidate.usageSummary.remainingPercent > switchRemainingThreshold).sort((a, b) => compareAutoRollCandidates(a, b, priorityRanks))[0] ?? null;
|
|
80324
|
+
if (!selected) selected = candidates.filter((candidate) => !candidate.usageSummary.hasUsage).sort((a, b) => compareAutoRollCandidates(a, b, priorityRanks))[0] ?? null;
|
|
80089
80325
|
if (!selected) return null;
|
|
80090
80326
|
if (selected.usageSummary.hasUsage && currentSummary.hasUsage) {
|
|
80091
|
-
if (selected.usageSummary.
|
|
80092
|
-
if (selected.usageSummary.
|
|
80327
|
+
if (selected.usageSummary.remainingPercent < currentSummary.remainingPercent) return null;
|
|
80328
|
+
if (selected.usageSummary.remainingPercent === currentSummary.remainingPercent) {
|
|
80093
80329
|
if ((selected.usageSummary.resetsInSeconds ?? Number.POSITIVE_INFINITY) >= (currentSummary.resetsInSeconds ?? Number.POSITIVE_INFINITY)) return null;
|
|
80094
80330
|
}
|
|
80095
80331
|
}
|
|
@@ -81812,12 +82048,12 @@ var TelegramBridge = class {
|
|
|
81812
82048
|
}
|
|
81813
82049
|
buildRuntimeLockPath(token) {
|
|
81814
82050
|
const tokenHash = createHash("sha1").update(token).digest("hex").slice(0, 16);
|
|
81815
|
-
return
|
|
82051
|
+
return nodePath.join(getUserDataDir(), `${TELEGRAM_BRIDGE_LOCK_FILE_PREFIX}-${tokenHash}.lock`);
|
|
81816
82052
|
}
|
|
81817
82053
|
async acquireRuntimeLock(token) {
|
|
81818
82054
|
if (this.runtimeLockPath) return;
|
|
81819
82055
|
const lockPath = this.buildRuntimeLockPath(token);
|
|
81820
|
-
await fs$1.mkdir(
|
|
82056
|
+
await fs$1.mkdir(nodePath.dirname(lockPath), { recursive: true });
|
|
81821
82057
|
const payload = JSON.stringify({
|
|
81822
82058
|
pid: process.pid,
|
|
81823
82059
|
parentPid: process.ppid > 0 ? process.ppid : null,
|
|
@@ -82037,13 +82273,26 @@ function createTelegramBridge(options) {
|
|
|
82037
82273
|
return new TelegramBridge(options);
|
|
82038
82274
|
}
|
|
82039
82275
|
//#endregion
|
|
82040
|
-
//#region src/codex/officialAppRestart.ts
|
|
82276
|
+
//#region ../../packages/runtime-codex/src/codex/officialAppRestart.ts
|
|
82041
82277
|
const RESTART_COOLDOWN_MS = 6e4;
|
|
82042
82278
|
const COMMAND_TIMEOUT_MS = 3e3;
|
|
82043
|
-
const
|
|
82279
|
+
const EXIT_WAIT_MS = 1e4;
|
|
82280
|
+
const LAUNCH_WAIT_MS = 15e3;
|
|
82044
82281
|
const POLL_INTERVAL_MS = 250;
|
|
82282
|
+
const APP_DISCOVERY_CACHE_TTL_MS = 6e4;
|
|
82283
|
+
const APP_DISCOVERY_MISS_CACHE_TTL_MS = 5e3;
|
|
82045
82284
|
let restartPromise = null;
|
|
82046
82285
|
let lastRestartStartedAt = 0;
|
|
82286
|
+
let profileActionLock = Promise.resolve();
|
|
82287
|
+
let appDiscoveryCache = null;
|
|
82288
|
+
function logOfficialCodexRestart(level, message, context) {
|
|
82289
|
+
const logger = level === "warn" ? console.warn : level === "error" ? console.error : console.info;
|
|
82290
|
+
if (context && Object.keys(context).length > 0) {
|
|
82291
|
+
logger(`[official-codex-restart] ${message}`, context);
|
|
82292
|
+
return;
|
|
82293
|
+
}
|
|
82294
|
+
logger(`[official-codex-restart] ${message}`);
|
|
82295
|
+
}
|
|
82047
82296
|
function runCommand(command, args, timeoutMs = COMMAND_TIMEOUT_MS) {
|
|
82048
82297
|
return new Promise((resolve) => {
|
|
82049
82298
|
const child = spawn(command, args, { stdio: [
|
|
@@ -82084,15 +82333,252 @@ function runCommand(command, args, timeoutMs = COMMAND_TIMEOUT_MS) {
|
|
|
82084
82333
|
});
|
|
82085
82334
|
});
|
|
82086
82335
|
}
|
|
82087
|
-
function
|
|
82088
|
-
|
|
82336
|
+
function enqueueProfileAction(work) {
|
|
82337
|
+
const run = profileActionLock.then(work, work);
|
|
82338
|
+
profileActionLock = run.then(() => void 0, () => void 0);
|
|
82339
|
+
return run;
|
|
82340
|
+
}
|
|
82341
|
+
async function readProcessRows() {
|
|
82342
|
+
const result = await runCommand("/bin/ps", ["-axo", "pid=,ppid=,lstart=,args="], 2e3);
|
|
82343
|
+
if (result.code !== 0) return [];
|
|
82344
|
+
return result.stdout.split(/\r?\n/).flatMap((line) => {
|
|
82345
|
+
const match = line.match(/^\s*(\d+)\s+(\d+)\s+([A-Z][a-z]{2}\s+[A-Z][a-z]{2}\s+\d+\s+\d+:\d+:\d+\s+\d{4})\s+(.+)$/);
|
|
82346
|
+
if (!match) return [];
|
|
82347
|
+
const pid = Number(match[1]);
|
|
82348
|
+
const ppid = Number(match[2]);
|
|
82349
|
+
const parsedStartedAt = Date.parse(match[3] ?? "");
|
|
82350
|
+
const startedAt = Number.isFinite(parsedStartedAt) ? parsedStartedAt : null;
|
|
82351
|
+
const args = match[4] ?? "";
|
|
82352
|
+
if (!Number.isInteger(pid) || !Number.isInteger(ppid) || !args) return [];
|
|
82353
|
+
return [{
|
|
82354
|
+
pid,
|
|
82355
|
+
ppid,
|
|
82356
|
+
startedAt,
|
|
82357
|
+
args
|
|
82358
|
+
}];
|
|
82359
|
+
});
|
|
82360
|
+
}
|
|
82361
|
+
function getMainExecutablePath(candidate) {
|
|
82362
|
+
return nodePath.join(candidate.appPath, "Contents", "MacOS", candidate.executableName);
|
|
82363
|
+
}
|
|
82364
|
+
function isMainProcessRow(row, candidate) {
|
|
82365
|
+
return row.args.includes(getMainExecutablePath(candidate));
|
|
82366
|
+
}
|
|
82367
|
+
function findAppServerPid(rows, mainPid) {
|
|
82368
|
+
return rows.find((row) => row.ppid === mainPid && row.args.includes("/Contents/Resources/codex app-server"))?.pid ?? null;
|
|
82369
|
+
}
|
|
82370
|
+
async function processMatchesProfileHome(pid, profileHome) {
|
|
82371
|
+
const result = await runCommand("/bin/ps", [
|
|
82372
|
+
"eww",
|
|
82373
|
+
"-p",
|
|
82374
|
+
String(pid),
|
|
82375
|
+
"-o",
|
|
82376
|
+
"command="
|
|
82377
|
+
], 2e3);
|
|
82378
|
+
if (result.code !== 0) return false;
|
|
82379
|
+
return result.stdout.includes(`CODEX_HOME=${profileHome}`) || result.stdout.includes(profileHome);
|
|
82380
|
+
}
|
|
82381
|
+
async function findAppServerPidForProfile(rows, mainPid, profileHome) {
|
|
82382
|
+
const candidates = rows.filter((row) => row.ppid === mainPid && row.args.includes("/Contents/Resources/codex app-server"));
|
|
82383
|
+
for (const row of candidates) if (row.args.includes(profileHome) || await processMatchesProfileHome(row.pid, profileHome)) return row.pid;
|
|
82384
|
+
return null;
|
|
82385
|
+
}
|
|
82386
|
+
async function describeUnmanagedProfileHint(appServerPid) {
|
|
82387
|
+
if (!appServerPid) return {};
|
|
82388
|
+
const result = await runCommand("/bin/ps", [
|
|
82389
|
+
"eww",
|
|
82390
|
+
"-p",
|
|
82391
|
+
String(appServerPid),
|
|
82392
|
+
"-o",
|
|
82393
|
+
"command="
|
|
82394
|
+
], 2e3);
|
|
82395
|
+
if (result.code !== 0) return {};
|
|
82396
|
+
const telemetryMatch = result.stdout.match(/CODEX_TELEMETRY_LABEL=codexuse-profile-([^\s]+)/);
|
|
82397
|
+
if (telemetryMatch?.[1]) {
|
|
82398
|
+
const encodedProfileName = telemetryMatch[1];
|
|
82399
|
+
let profileName = encodedProfileName;
|
|
82400
|
+
try {
|
|
82401
|
+
profileName = decodeURIComponent(encodedProfileName);
|
|
82402
|
+
} catch {
|
|
82403
|
+
profileName = encodedProfileName;
|
|
82404
|
+
}
|
|
82405
|
+
return {
|
|
82406
|
+
profileName,
|
|
82407
|
+
profileMatchSource: "telemetry-label"
|
|
82408
|
+
};
|
|
82409
|
+
}
|
|
82410
|
+
const profileHomeMatch = result.stdout.match(/profile-homes\/([^\s]+)/);
|
|
82411
|
+
if (profileHomeMatch?.[1]) return {
|
|
82412
|
+
profileName: profileHomeMatch[1],
|
|
82413
|
+
profileMatchSource: "profile-home"
|
|
82414
|
+
};
|
|
82415
|
+
return {};
|
|
82416
|
+
}
|
|
82417
|
+
function getDescendantPids(rootPid, rows) {
|
|
82418
|
+
const descendants = [];
|
|
82419
|
+
const queue = [rootPid];
|
|
82420
|
+
const seen = /* @__PURE__ */ new Set();
|
|
82421
|
+
while (queue.length > 0) {
|
|
82422
|
+
const current = queue.shift();
|
|
82423
|
+
if (seen.has(current)) continue;
|
|
82424
|
+
seen.add(current);
|
|
82425
|
+
for (const row of rows) {
|
|
82426
|
+
if (row.ppid !== current || seen.has(row.pid)) continue;
|
|
82427
|
+
descendants.push(row.pid);
|
|
82428
|
+
queue.push(row.pid);
|
|
82429
|
+
}
|
|
82430
|
+
}
|
|
82431
|
+
return descendants;
|
|
82432
|
+
}
|
|
82433
|
+
async function waitForNewMainPid(candidate, previousPids) {
|
|
82434
|
+
const deadline = Date.now() + LAUNCH_WAIT_MS;
|
|
82435
|
+
while (Date.now() < deadline) {
|
|
82436
|
+
const pid = (await readProcessRows()).find((row) => isMainProcessRow(row, candidate) && !previousPids.has(row.pid))?.pid ?? null;
|
|
82437
|
+
if (pid !== null) return pid;
|
|
82438
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
82439
|
+
}
|
|
82440
|
+
return null;
|
|
82441
|
+
}
|
|
82442
|
+
async function waitForMainPid(candidate, pid, timeoutMs) {
|
|
82443
|
+
const deadline = Date.now() + timeoutMs;
|
|
82444
|
+
while (Date.now() < deadline) {
|
|
82445
|
+
if ((await readProcessRows()).some((row) => row.pid === pid && isMainProcessRow(row, candidate))) return true;
|
|
82446
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
82447
|
+
}
|
|
82448
|
+
return false;
|
|
82449
|
+
}
|
|
82450
|
+
async function waitForAppServerPid(mainPid, profileHome) {
|
|
82451
|
+
const deadline = Date.now() + LAUNCH_WAIT_MS;
|
|
82452
|
+
while (Date.now() < deadline) {
|
|
82453
|
+
const pid = await findAppServerPidForProfile(await readProcessRows(), mainPid, profileHome);
|
|
82454
|
+
if (pid !== null) return pid;
|
|
82455
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
82456
|
+
}
|
|
82457
|
+
return null;
|
|
82458
|
+
}
|
|
82459
|
+
async function waitForMainPidExit(pid, timeoutMs) {
|
|
82460
|
+
const deadline = Date.now() + timeoutMs;
|
|
82461
|
+
while (Date.now() < deadline) {
|
|
82462
|
+
if (!(await readProcessRows()).some((row) => row.pid === pid)) return true;
|
|
82463
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
82464
|
+
}
|
|
82465
|
+
return false;
|
|
82466
|
+
}
|
|
82467
|
+
async function focusMainPid(pid) {
|
|
82468
|
+
return (await runCommand("/usr/bin/osascript", [
|
|
82469
|
+
"-e",
|
|
82470
|
+
`tell application "System Events" to repeat with proc in (processes whose unix id is ${pid})`,
|
|
82471
|
+
"-e",
|
|
82472
|
+
"set frontmost of proc to true",
|
|
82473
|
+
"-e",
|
|
82474
|
+
"end repeat"
|
|
82475
|
+
], 5e3)).code === 0;
|
|
82476
|
+
}
|
|
82477
|
+
function managedInstanceToPayload(instance, running, appServerPid = instance.appServerPid, startedAt = null) {
|
|
82478
|
+
const runningStartedAt = startedAt ?? instance.launchedAt;
|
|
82479
|
+
return {
|
|
82480
|
+
profileName: instance.profileName,
|
|
82481
|
+
profileKey: instance.profileKey,
|
|
82482
|
+
appPath: instance.appPath,
|
|
82483
|
+
bundleId: instance.bundleId,
|
|
82484
|
+
pid: running ? instance.pid : null,
|
|
82485
|
+
appServerPid: running ? appServerPid : null,
|
|
82486
|
+
running,
|
|
82487
|
+
startedAt: running ? toIso(runningStartedAt) : null,
|
|
82488
|
+
uptimeMs: running && typeof runningStartedAt === "number" ? Math.max(0, Date.now() - runningStartedAt) : null,
|
|
82489
|
+
launchedAt: toIso(instance.launchedAt),
|
|
82490
|
+
lastVerifiedAt: toIso(instance.lastVerifiedAt),
|
|
82491
|
+
lastStatus: instance.lastStatus,
|
|
82492
|
+
lastError: instance.lastError
|
|
82493
|
+
};
|
|
82494
|
+
}
|
|
82495
|
+
async function patchManagedInstance(instance) {
|
|
82496
|
+
await patchAppState({ officialCodex: { instancesByProfileName: { [instance.profileName]: instance } } });
|
|
82497
|
+
}
|
|
82498
|
+
async function readManagedInstance(profileName) {
|
|
82499
|
+
return (await getAppState()).officialCodex.instancesByProfileName[profileName] ?? null;
|
|
82500
|
+
}
|
|
82501
|
+
async function resolveInstanceRuntimeState(args) {
|
|
82502
|
+
const pid = args.instance.pid;
|
|
82503
|
+
if (!pid) return {
|
|
82504
|
+
running: false,
|
|
82505
|
+
appServerPid: null,
|
|
82506
|
+
startedAt: null
|
|
82507
|
+
};
|
|
82508
|
+
const mainRow = args.rows.find((row) => row.pid === pid);
|
|
82509
|
+
if (!mainRow || !isMainProcessRow(mainRow, args.candidate)) return {
|
|
82510
|
+
running: false,
|
|
82511
|
+
appServerPid: null,
|
|
82512
|
+
startedAt: null
|
|
82513
|
+
};
|
|
82514
|
+
const appServerPid = args.instance.profileHome ? await findAppServerPidForProfile(args.rows, pid, args.instance.profileHome) : findAppServerPid(args.rows, pid);
|
|
82515
|
+
if (args.instance.profileHome && appServerPid === null) return {
|
|
82516
|
+
running: false,
|
|
82517
|
+
appServerPid: null,
|
|
82518
|
+
startedAt: null
|
|
82519
|
+
};
|
|
82520
|
+
return {
|
|
82521
|
+
running: true,
|
|
82522
|
+
appServerPid,
|
|
82523
|
+
startedAt: mainRow.startedAt
|
|
82524
|
+
};
|
|
82525
|
+
}
|
|
82526
|
+
async function openCodexWithProfileHome(candidate, profileHome, profileName, previousPids) {
|
|
82527
|
+
const executablePath = getMainExecutablePath(candidate);
|
|
82528
|
+
if (!existsSync(executablePath)) return {
|
|
82529
|
+
opened: false,
|
|
82530
|
+
pid: null
|
|
82531
|
+
};
|
|
82532
|
+
const telemetryLabel = `codexuse-profile-${encodeURIComponent(profileName)}`;
|
|
82533
|
+
let child;
|
|
82534
|
+
try {
|
|
82535
|
+
child = spawn(executablePath, [], {
|
|
82536
|
+
detached: true,
|
|
82537
|
+
env: {
|
|
82538
|
+
...process.env,
|
|
82539
|
+
CODEX_HOME: profileHome,
|
|
82540
|
+
CODEX_TELEMETRY_LABEL: telemetryLabel
|
|
82541
|
+
},
|
|
82542
|
+
stdio: "ignore"
|
|
82543
|
+
});
|
|
82544
|
+
} catch {
|
|
82545
|
+
return {
|
|
82546
|
+
opened: false,
|
|
82547
|
+
pid: null
|
|
82548
|
+
};
|
|
82549
|
+
}
|
|
82550
|
+
const childPid = child.pid ?? null;
|
|
82551
|
+
child.unref();
|
|
82552
|
+
if (await new Promise((resolve) => {
|
|
82553
|
+
let settled = false;
|
|
82554
|
+
const settle = (failed) => {
|
|
82555
|
+
if (settled) return;
|
|
82556
|
+
settled = true;
|
|
82557
|
+
resolve(failed);
|
|
82558
|
+
};
|
|
82559
|
+
child.once("error", () => settle(true));
|
|
82560
|
+
setTimeout(() => settle(false), POLL_INTERVAL_MS);
|
|
82561
|
+
})) return {
|
|
82562
|
+
opened: false,
|
|
82563
|
+
pid: null
|
|
82564
|
+
};
|
|
82565
|
+
if (childPid !== null) {
|
|
82566
|
+
if (await waitForMainPid(candidate, childPid, 2e3)) return {
|
|
82567
|
+
opened: true,
|
|
82568
|
+
pid: childPid
|
|
82569
|
+
};
|
|
82570
|
+
}
|
|
82571
|
+
return {
|
|
82572
|
+
opened: true,
|
|
82573
|
+
pid: await waitForNewMainPid(candidate, previousPids)
|
|
82574
|
+
};
|
|
82089
82575
|
}
|
|
82090
|
-
async function
|
|
82091
|
-
const plistPath =
|
|
82576
|
+
async function readBundleString(appPath, key) {
|
|
82577
|
+
const plistPath = nodePath.join(appPath, "Contents", "Info.plist");
|
|
82092
82578
|
if (!existsSync(plistPath)) return null;
|
|
82093
82579
|
const result = await runCommand("/usr/bin/plutil", [
|
|
82094
82580
|
"-extract",
|
|
82095
|
-
|
|
82581
|
+
key,
|
|
82096
82582
|
"raw",
|
|
82097
82583
|
"-o",
|
|
82098
82584
|
"-",
|
|
@@ -82103,11 +82589,13 @@ async function readBundleIdentifier(appPath) {
|
|
|
82103
82589
|
return bundleId.length > 0 ? bundleId : null;
|
|
82104
82590
|
}
|
|
82105
82591
|
async function discoverCodexAppCandidates() {
|
|
82592
|
+
const now = Date.now();
|
|
82593
|
+
const envPath = process.env.CODEXUSE_OFFICIAL_CODEX_APP_PATH?.trim() || null;
|
|
82594
|
+
if (appDiscoveryCache && appDiscoveryCache.envPath === envPath && appDiscoveryCache.expiresAt > now) return appDiscoveryCache.candidates;
|
|
82106
82595
|
const rawPaths = /* @__PURE__ */ new Set();
|
|
82107
|
-
const envPath = process.env.CODEXUSE_OFFICIAL_CODEX_APP_PATH?.trim();
|
|
82108
82596
|
if (envPath) rawPaths.add(envPath);
|
|
82109
82597
|
rawPaths.add("/Applications/Codex.app");
|
|
82110
|
-
rawPaths.add(
|
|
82598
|
+
rawPaths.add(nodePath.join(homedir(), "Applications", "Codex.app"));
|
|
82111
82599
|
const mdfind = await runCommand("/usr/bin/mdfind", ["kMDItemFSName == 'Codex.app'"], 1500);
|
|
82112
82600
|
if (mdfind.code === 0) for (const line of mdfind.stdout.split(/\r?\n/)) {
|
|
82113
82601
|
const trimmed = line.trim();
|
|
@@ -82115,85 +82603,716 @@ async function discoverCodexAppCandidates() {
|
|
|
82115
82603
|
}
|
|
82116
82604
|
const candidates = [];
|
|
82117
82605
|
for (const appPath of rawPaths) {
|
|
82118
|
-
if (!existsSync(appPath) ||
|
|
82119
|
-
const bundleId = await
|
|
82606
|
+
if (!existsSync(appPath) || nodePath.basename(appPath) !== "Codex.app") continue;
|
|
82607
|
+
const bundleId = await readBundleString(appPath, "CFBundleIdentifier");
|
|
82120
82608
|
if (!bundleId || bundleId.toLowerCase().includes("codexuse")) continue;
|
|
82121
82609
|
candidates.push({
|
|
82122
82610
|
appPath,
|
|
82123
|
-
bundleId
|
|
82611
|
+
bundleId,
|
|
82612
|
+
version: await readBundleString(appPath, "CFBundleShortVersionString"),
|
|
82613
|
+
executableName: await readBundleString(appPath, "CFBundleExecutable") ?? "Codex"
|
|
82124
82614
|
});
|
|
82125
82615
|
}
|
|
82126
|
-
|
|
82616
|
+
const sorted = candidates.sort((a, b) => a.appPath.localeCompare(b.appPath));
|
|
82617
|
+
appDiscoveryCache = {
|
|
82618
|
+
envPath,
|
|
82619
|
+
expiresAt: now + (sorted.length > 0 ? APP_DISCOVERY_CACHE_TTL_MS : APP_DISCOVERY_MISS_CACHE_TTL_MS),
|
|
82620
|
+
candidates: sorted
|
|
82621
|
+
};
|
|
82622
|
+
return sorted;
|
|
82127
82623
|
}
|
|
82128
82624
|
async function getRunningCodexPids(candidate) {
|
|
82129
82625
|
const result = await runCommand("/bin/ps", ["-axo", "pid=,args="], 2e3);
|
|
82130
82626
|
const pids = /* @__PURE__ */ new Set();
|
|
82131
|
-
const
|
|
82627
|
+
const appContentsRoot = `${nodePath.join(candidate.appPath, "Contents")}${nodePath.sep}`;
|
|
82628
|
+
if (result.code === 0) for (const line of result.stdout.split(/\r?\n/)) {
|
|
82629
|
+
const match = line.match(/^\s*(\d+)\s+(.+)$/);
|
|
82630
|
+
if (!match) continue;
|
|
82631
|
+
const pid = Number(match[1]);
|
|
82632
|
+
const args = match[2] ?? "";
|
|
82633
|
+
if (Number.isInteger(pid) && args.includes(appContentsRoot)) pids.add(pid);
|
|
82634
|
+
}
|
|
82635
|
+
return Array.from(pids).sort((a, b) => a - b);
|
|
82636
|
+
}
|
|
82637
|
+
async function getRunningCodexMainPids(candidate) {
|
|
82638
|
+
const result = await runCommand("/bin/ps", ["-axo", "pid=,args="], 2e3);
|
|
82639
|
+
const pids = /* @__PURE__ */ new Set();
|
|
82640
|
+
const executablePath = nodePath.join(candidate.appPath, "Contents", "MacOS", candidate.executableName);
|
|
82132
82641
|
if (result.code === 0) for (const line of result.stdout.split(/\r?\n/)) {
|
|
82133
82642
|
const match = line.match(/^\s*(\d+)\s+(.+)$/);
|
|
82134
82643
|
if (!match) continue;
|
|
82135
82644
|
const pid = Number(match[1]);
|
|
82136
82645
|
const args = match[2] ?? "";
|
|
82137
|
-
if (Number.isInteger(pid) && args.includes(
|
|
82646
|
+
if (Number.isInteger(pid) && args.includes(executablePath)) pids.add(pid);
|
|
82138
82647
|
}
|
|
82139
|
-
return Array.from(pids);
|
|
82648
|
+
return Array.from(pids).sort((a, b) => a - b);
|
|
82140
82649
|
}
|
|
82141
|
-
async function
|
|
82142
|
-
const
|
|
82650
|
+
async function resolveCodexAppTarget() {
|
|
82651
|
+
const candidates = await discoverCodexAppCandidates();
|
|
82652
|
+
const fallback = candidates[0] ?? null;
|
|
82653
|
+
if (!fallback) return {
|
|
82654
|
+
candidate: null,
|
|
82655
|
+
runningPids: [],
|
|
82656
|
+
mainPids: []
|
|
82657
|
+
};
|
|
82658
|
+
for (const candidate of candidates) {
|
|
82659
|
+
const [runningPids, mainPids] = await Promise.all([getRunningCodexPids(candidate), getRunningCodexMainPids(candidate)]);
|
|
82660
|
+
if (mainPids.length > 0) return {
|
|
82661
|
+
candidate,
|
|
82662
|
+
runningPids,
|
|
82663
|
+
mainPids
|
|
82664
|
+
};
|
|
82665
|
+
}
|
|
82666
|
+
const [runningPids, mainPids] = await Promise.all([getRunningCodexPids(fallback), getRunningCodexMainPids(fallback)]);
|
|
82667
|
+
return {
|
|
82668
|
+
candidate: fallback,
|
|
82669
|
+
runningPids,
|
|
82670
|
+
mainPids
|
|
82671
|
+
};
|
|
82672
|
+
}
|
|
82673
|
+
async function waitForCodexExit(candidate, timeoutMs) {
|
|
82674
|
+
const deadline = Date.now() + timeoutMs;
|
|
82143
82675
|
while (Date.now() < deadline) {
|
|
82144
82676
|
if ((await getRunningCodexPids(candidate)).length === 0) return true;
|
|
82145
82677
|
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
82146
82678
|
}
|
|
82147
82679
|
return false;
|
|
82148
82680
|
}
|
|
82149
|
-
async function
|
|
82150
|
-
|
|
82681
|
+
async function signalPids(pids, signal) {
|
|
82682
|
+
const signaled = [];
|
|
82683
|
+
for (const pid of pids) try {
|
|
82684
|
+
process.kill(pid, signal);
|
|
82685
|
+
signaled.push(pid);
|
|
82686
|
+
} catch {}
|
|
82687
|
+
return signaled;
|
|
82151
82688
|
}
|
|
82152
82689
|
async function openCodex(candidate) {
|
|
82153
82690
|
if ((await runCommand("/usr/bin/open", ["-b", candidate.bundleId])).code === 0) return true;
|
|
82154
82691
|
return (await runCommand("/usr/bin/open", [candidate.appPath])).code === 0;
|
|
82155
82692
|
}
|
|
82156
|
-
async function
|
|
82157
|
-
|
|
82158
|
-
|
|
82159
|
-
|
|
82693
|
+
async function waitForCodexLaunch(candidate) {
|
|
82694
|
+
const deadline = Date.now() + LAUNCH_WAIT_MS;
|
|
82695
|
+
while (Date.now() < deadline) {
|
|
82696
|
+
const pid = (await getRunningCodexMainPids(candidate))[0] ?? null;
|
|
82697
|
+
if (pid !== null) return pid;
|
|
82698
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
82699
|
+
}
|
|
82700
|
+
return null;
|
|
82701
|
+
}
|
|
82702
|
+
function toIso(value) {
|
|
82703
|
+
return typeof value === "number" && Number.isFinite(value) ? new Date(value).toISOString() : null;
|
|
82704
|
+
}
|
|
82705
|
+
async function rememberProfileSwitch(profileKey) {
|
|
82706
|
+
await patchAppState({ officialCodex: {
|
|
82707
|
+
lastProfileSwitchAt: Date.now(),
|
|
82708
|
+
lastProfileSwitchProfileKey: profileKey
|
|
82709
|
+
} });
|
|
82710
|
+
}
|
|
82711
|
+
async function rememberRestartResult(args) {
|
|
82712
|
+
const now = Date.now();
|
|
82713
|
+
const verified = args.status === "restarted" || args.status === "started";
|
|
82714
|
+
await patchAppState({ officialCodex: {
|
|
82715
|
+
lastVerifiedLaunchAt: verified ? now : void 0,
|
|
82716
|
+
lastVerifiedLaunchProfileKey: verified ? args.profileKey ?? null : void 0,
|
|
82717
|
+
lastObservedPid: verified ? args.pid ?? null : void 0,
|
|
82718
|
+
lastRestartStatus: args.status,
|
|
82719
|
+
lastRestartReason: args.reason ?? null
|
|
82720
|
+
} });
|
|
82721
|
+
}
|
|
82722
|
+
async function restartOfficialCodexAppOnce(options) {
|
|
82723
|
+
if (process.platform !== "darwin") {
|
|
82724
|
+
await rememberRestartResult({
|
|
82725
|
+
status: "skipped",
|
|
82726
|
+
reason: "unsupported-platform"
|
|
82727
|
+
});
|
|
82728
|
+
return {
|
|
82729
|
+
status: "skipped",
|
|
82730
|
+
reason: "unsupported-platform"
|
|
82731
|
+
};
|
|
82732
|
+
}
|
|
82733
|
+
const now = Date.now();
|
|
82734
|
+
if (options.source !== "manual" && lastRestartStartedAt > 0 && now - lastRestartStartedAt < RESTART_COOLDOWN_MS) {
|
|
82735
|
+
await rememberRestartResult({
|
|
82736
|
+
status: "skipped",
|
|
82737
|
+
reason: "cooldown",
|
|
82738
|
+
profileKey: options.currentProfileKey
|
|
82739
|
+
});
|
|
82740
|
+
return {
|
|
82741
|
+
status: "skipped",
|
|
82742
|
+
reason: "cooldown"
|
|
82743
|
+
};
|
|
82744
|
+
}
|
|
82745
|
+
const { candidate, runningPids, mainPids } = await resolveCodexAppTarget();
|
|
82746
|
+
if (!candidate) {
|
|
82747
|
+
await rememberRestartResult({
|
|
82748
|
+
status: "skipped",
|
|
82749
|
+
reason: "official-codex-app-not-found",
|
|
82750
|
+
profileKey: options.currentProfileKey
|
|
82751
|
+
});
|
|
82752
|
+
return {
|
|
82753
|
+
status: "skipped",
|
|
82754
|
+
reason: "official-codex-app-not-found"
|
|
82755
|
+
};
|
|
82756
|
+
}
|
|
82757
|
+
const appIsRunning = mainPids.length > 0;
|
|
82758
|
+
if (!appIsRunning && !options.launchIfNotRunning) {
|
|
82759
|
+
await rememberRestartResult({
|
|
82760
|
+
status: "skipped",
|
|
82761
|
+
reason: "official-codex-app-not-running",
|
|
82762
|
+
profileKey: options.currentProfileKey
|
|
82763
|
+
});
|
|
82764
|
+
return {
|
|
82765
|
+
status: "skipped",
|
|
82766
|
+
reason: "official-codex-app-not-running"
|
|
82767
|
+
};
|
|
82768
|
+
}
|
|
82769
|
+
if (options.source !== "manual") lastRestartStartedAt = Date.now();
|
|
82770
|
+
if (appIsRunning) {
|
|
82771
|
+
logOfficialCodexRestart("warn", "Force killing official Codex before relaunch.", {
|
|
82772
|
+
appPath: candidate.appPath,
|
|
82773
|
+
bundleId: candidate.bundleId,
|
|
82774
|
+
source: options.source,
|
|
82775
|
+
runningPids,
|
|
82776
|
+
mainPids
|
|
82777
|
+
});
|
|
82778
|
+
const killedPids = await signalPids(runningPids, "SIGKILL");
|
|
82779
|
+
logOfficialCodexRestart("warn", "Sent SIGKILL to official Codex pids.", {
|
|
82780
|
+
appPath: candidate.appPath,
|
|
82781
|
+
bundleId: candidate.bundleId,
|
|
82782
|
+
source: options.source,
|
|
82783
|
+
killedPids
|
|
82784
|
+
});
|
|
82785
|
+
if (!await waitForCodexExit(candidate, EXIT_WAIT_MS)) {
|
|
82786
|
+
const remainingPids = await getRunningCodexPids(candidate);
|
|
82787
|
+
logOfficialCodexRestart("error", "Official Codex pids remained after force kill.", {
|
|
82788
|
+
appPath: candidate.appPath,
|
|
82789
|
+
bundleId: candidate.bundleId,
|
|
82790
|
+
source: options.source,
|
|
82791
|
+
remainingPids
|
|
82792
|
+
});
|
|
82793
|
+
const failed = {
|
|
82794
|
+
status: "failed",
|
|
82795
|
+
appPath: candidate.appPath,
|
|
82796
|
+
bundleId: candidate.bundleId,
|
|
82797
|
+
reason: "force-quit-timeout",
|
|
82798
|
+
phase: "quit"
|
|
82799
|
+
};
|
|
82800
|
+
await rememberRestartResult({
|
|
82801
|
+
status: failed.status,
|
|
82802
|
+
reason: failed.reason,
|
|
82803
|
+
profileKey: options.currentProfileKey
|
|
82804
|
+
});
|
|
82805
|
+
return failed;
|
|
82806
|
+
}
|
|
82807
|
+
logOfficialCodexRestart("info", "Official Codex exited after force kill.", {
|
|
82808
|
+
appPath: candidate.appPath,
|
|
82809
|
+
bundleId: candidate.bundleId,
|
|
82810
|
+
source: options.source
|
|
82811
|
+
});
|
|
82812
|
+
}
|
|
82813
|
+
if (!await openCodex(candidate)) {
|
|
82814
|
+
const failed = {
|
|
82815
|
+
status: "failed",
|
|
82816
|
+
appPath: candidate.appPath,
|
|
82817
|
+
bundleId: candidate.bundleId,
|
|
82818
|
+
reason: "open-failed",
|
|
82819
|
+
phase: "launch"
|
|
82820
|
+
};
|
|
82821
|
+
await rememberRestartResult({
|
|
82822
|
+
status: failed.status,
|
|
82823
|
+
reason: failed.reason,
|
|
82824
|
+
profileKey: options.currentProfileKey
|
|
82825
|
+
});
|
|
82826
|
+
return failed;
|
|
82827
|
+
}
|
|
82828
|
+
const launchedPid = await waitForCodexLaunch(candidate);
|
|
82829
|
+
if (launchedPid === null) {
|
|
82830
|
+
logOfficialCodexRestart("error", "Official Codex launch was not verified.", {
|
|
82831
|
+
appPath: candidate.appPath,
|
|
82832
|
+
bundleId: candidate.bundleId,
|
|
82833
|
+
source: options.source
|
|
82834
|
+
});
|
|
82835
|
+
const failed = {
|
|
82836
|
+
status: "failed",
|
|
82837
|
+
appPath: candidate.appPath,
|
|
82838
|
+
bundleId: candidate.bundleId,
|
|
82839
|
+
reason: "launch-timeout",
|
|
82840
|
+
phase: "launch"
|
|
82841
|
+
};
|
|
82842
|
+
await rememberRestartResult({
|
|
82843
|
+
status: failed.status,
|
|
82844
|
+
reason: failed.reason,
|
|
82845
|
+
profileKey: options.currentProfileKey
|
|
82846
|
+
});
|
|
82847
|
+
return failed;
|
|
82848
|
+
}
|
|
82849
|
+
const status = appIsRunning ? "restarted" : "started";
|
|
82850
|
+
logOfficialCodexRestart("info", "Official Codex launch verified.", {
|
|
82851
|
+
appPath: candidate.appPath,
|
|
82852
|
+
bundleId: candidate.bundleId,
|
|
82853
|
+
source: options.source,
|
|
82854
|
+
status,
|
|
82855
|
+
pid: launchedPid
|
|
82856
|
+
});
|
|
82857
|
+
await rememberRestartResult({
|
|
82858
|
+
status,
|
|
82859
|
+
profileKey: options.currentProfileKey,
|
|
82860
|
+
pid: launchedPid
|
|
82861
|
+
});
|
|
82862
|
+
return {
|
|
82863
|
+
status,
|
|
82864
|
+
appPath: candidate.appPath,
|
|
82865
|
+
bundleId: candidate.bundleId,
|
|
82866
|
+
pid: launchedPid
|
|
82160
82867
|
};
|
|
82161
|
-
|
|
82162
|
-
|
|
82163
|
-
|
|
82868
|
+
}
|
|
82869
|
+
async function getOfficialCodexSyncStatus(currentProfileKey) {
|
|
82870
|
+
const stored = (await getAppState()).officialCodex;
|
|
82871
|
+
const base = {
|
|
82872
|
+
runningPids: [],
|
|
82873
|
+
lastProfileSwitchAt: toIso(stored.lastProfileSwitchAt),
|
|
82874
|
+
lastVerifiedLaunchAt: toIso(stored.lastVerifiedLaunchAt),
|
|
82875
|
+
lastRestartStatus: stored.lastRestartStatus,
|
|
82876
|
+
lastRestartReason: stored.lastRestartReason
|
|
82164
82877
|
};
|
|
82165
|
-
|
|
82166
|
-
|
|
82167
|
-
|
|
82168
|
-
reason: "official-codex-app-not-found"
|
|
82878
|
+
if (process.platform !== "darwin") return {
|
|
82879
|
+
...base,
|
|
82880
|
+
state: "unsupported"
|
|
82169
82881
|
};
|
|
82170
|
-
|
|
82171
|
-
|
|
82172
|
-
|
|
82882
|
+
const { candidate, mainPids } = await resolveCodexAppTarget();
|
|
82883
|
+
if (!candidate) return {
|
|
82884
|
+
...base,
|
|
82885
|
+
state: "not-found"
|
|
82173
82886
|
};
|
|
82174
|
-
|
|
82175
|
-
|
|
82176
|
-
|
|
82177
|
-
status: "failed",
|
|
82887
|
+
if (mainPids.length === 0) return {
|
|
82888
|
+
...base,
|
|
82889
|
+
state: "not-running",
|
|
82178
82890
|
appPath: candidate.appPath,
|
|
82179
82891
|
bundleId: candidate.bundleId,
|
|
82180
|
-
|
|
82892
|
+
runningPids: mainPids
|
|
82181
82893
|
};
|
|
82182
|
-
|
|
82183
|
-
|
|
82894
|
+
const lastSwitchAt = stored.lastProfileSwitchAt;
|
|
82895
|
+
const lastLaunchAt = stored.lastVerifiedLaunchAt;
|
|
82896
|
+
const lastLaunchProfileKey = stored.lastVerifiedLaunchProfileKey;
|
|
82897
|
+
const stale = Boolean(currentProfileKey && lastSwitchAt && (!lastLaunchAt || lastLaunchAt < lastSwitchAt || lastLaunchProfileKey !== currentProfileKey));
|
|
82898
|
+
return {
|
|
82899
|
+
...base,
|
|
82900
|
+
state: stale ? "stale" : currentProfileKey && lastLaunchProfileKey === currentProfileKey ? "synced" : "unknown",
|
|
82184
82901
|
appPath: candidate.appPath,
|
|
82185
82902
|
bundleId: candidate.bundleId,
|
|
82186
|
-
|
|
82903
|
+
runningPids: mainPids
|
|
82187
82904
|
};
|
|
82905
|
+
}
|
|
82906
|
+
function recordOfficialCodexProfileSwitch(profileKey) {
|
|
82907
|
+
return rememberProfileSwitch(profileKey);
|
|
82908
|
+
}
|
|
82909
|
+
async function getOfficialCodexProfileInstances() {
|
|
82910
|
+
if (process.platform !== "darwin") return {
|
|
82911
|
+
state: "unsupported",
|
|
82912
|
+
appPath: null,
|
|
82913
|
+
bundleId: null,
|
|
82914
|
+
version: null,
|
|
82915
|
+
instances: [],
|
|
82916
|
+
unmanaged: []
|
|
82917
|
+
};
|
|
82918
|
+
const { candidate, mainPids } = await resolveCodexAppTarget();
|
|
82919
|
+
if (!candidate) return {
|
|
82920
|
+
state: "not-found",
|
|
82921
|
+
appPath: null,
|
|
82922
|
+
bundleId: null,
|
|
82923
|
+
version: null,
|
|
82924
|
+
instances: [],
|
|
82925
|
+
unmanaged: []
|
|
82926
|
+
};
|
|
82927
|
+
const [state, rows] = await Promise.all([getAppState(), readProcessRows()]);
|
|
82928
|
+
const managed = [];
|
|
82929
|
+
for (const instance of Object.values(state.officialCodex.instancesByProfileName)) {
|
|
82930
|
+
const runtime = await resolveInstanceRuntimeState({
|
|
82931
|
+
instance,
|
|
82932
|
+
candidate,
|
|
82933
|
+
rows
|
|
82934
|
+
});
|
|
82935
|
+
managed.push(managedInstanceToPayload(instance, runtime.running, runtime.appServerPid, runtime.startedAt));
|
|
82936
|
+
}
|
|
82937
|
+
managed.sort((a, b) => a.profileName.localeCompare(b.profileName));
|
|
82938
|
+
const managedPids = new Set(managed.flatMap((entry) => entry.running && entry.pid ? [entry.pid] : []));
|
|
82939
|
+
const unmanaged = [];
|
|
82940
|
+
for (const pid of mainPids.filter((entry) => !managedPids.has(entry))) {
|
|
82941
|
+
const appServerPid = findAppServerPid(rows, pid);
|
|
82942
|
+
const startedAt = rows.find((row) => row.pid === pid)?.startedAt ?? null;
|
|
82943
|
+
unmanaged.push({
|
|
82944
|
+
pid,
|
|
82945
|
+
appServerPid,
|
|
82946
|
+
startedAt: toIso(startedAt),
|
|
82947
|
+
uptimeMs: typeof startedAt === "number" ? Math.max(0, Date.now() - startedAt) : null,
|
|
82948
|
+
...await describeUnmanagedProfileHint(appServerPid)
|
|
82949
|
+
});
|
|
82950
|
+
}
|
|
82188
82951
|
return {
|
|
82189
|
-
|
|
82952
|
+
state: "found",
|
|
82190
82953
|
appPath: candidate.appPath,
|
|
82191
|
-
bundleId: candidate.bundleId
|
|
82954
|
+
bundleId: candidate.bundleId,
|
|
82955
|
+
version: candidate.version,
|
|
82956
|
+
instances: managed,
|
|
82957
|
+
unmanaged
|
|
82192
82958
|
};
|
|
82193
82959
|
}
|
|
82194
|
-
function
|
|
82960
|
+
function launchOfficialCodexProfileInstance(options) {
|
|
82961
|
+
return enqueueProfileAction(async () => {
|
|
82962
|
+
if (process.platform !== "darwin") return {
|
|
82963
|
+
status: "skipped",
|
|
82964
|
+
profileName: options.profileName,
|
|
82965
|
+
instance: null,
|
|
82966
|
+
reason: "unsupported-platform"
|
|
82967
|
+
};
|
|
82968
|
+
const { candidate, mainPids } = await resolveCodexAppTarget();
|
|
82969
|
+
if (!candidate) return {
|
|
82970
|
+
status: "skipped",
|
|
82971
|
+
profileName: options.profileName,
|
|
82972
|
+
instance: null,
|
|
82973
|
+
reason: "official-codex-app-not-found"
|
|
82974
|
+
};
|
|
82975
|
+
const existing = await readManagedInstance(options.profileName);
|
|
82976
|
+
if (existing) {
|
|
82977
|
+
const runtime = await resolveInstanceRuntimeState({
|
|
82978
|
+
instance: existing,
|
|
82979
|
+
candidate,
|
|
82980
|
+
rows: await readProcessRows()
|
|
82981
|
+
});
|
|
82982
|
+
if (runtime.running) {
|
|
82983
|
+
const verified = {
|
|
82984
|
+
...existing,
|
|
82985
|
+
profileHome: existing.profileHome ?? options.profileHome,
|
|
82986
|
+
appServerPid: runtime.appServerPid,
|
|
82987
|
+
lastVerifiedAt: Date.now(),
|
|
82988
|
+
lastStatus: "already-running",
|
|
82989
|
+
lastError: null
|
|
82990
|
+
};
|
|
82991
|
+
await patchManagedInstance(verified);
|
|
82992
|
+
return {
|
|
82993
|
+
status: "already-running",
|
|
82994
|
+
profileName: options.profileName,
|
|
82995
|
+
instance: managedInstanceToPayload(verified, true, runtime.appServerPid, runtime.startedAt),
|
|
82996
|
+
reason: null
|
|
82997
|
+
};
|
|
82998
|
+
}
|
|
82999
|
+
}
|
|
83000
|
+
const launch = await openCodexWithProfileHome(candidate, options.profileHome, options.profileName, new Set(mainPids));
|
|
83001
|
+
if (!launch.opened) {
|
|
83002
|
+
const failed = {
|
|
83003
|
+
profileName: options.profileName,
|
|
83004
|
+
profileKey: options.profileKey,
|
|
83005
|
+
profileHome: options.profileHome,
|
|
83006
|
+
appPath: candidate.appPath,
|
|
83007
|
+
bundleId: candidate.bundleId,
|
|
83008
|
+
pid: null,
|
|
83009
|
+
appServerPid: null,
|
|
83010
|
+
launchedAt: null,
|
|
83011
|
+
lastVerifiedAt: null,
|
|
83012
|
+
lastStatus: "failed",
|
|
83013
|
+
lastError: "open-failed"
|
|
83014
|
+
};
|
|
83015
|
+
await patchManagedInstance(failed);
|
|
83016
|
+
return {
|
|
83017
|
+
status: "failed",
|
|
83018
|
+
profileName: options.profileName,
|
|
83019
|
+
instance: managedInstanceToPayload(failed, false),
|
|
83020
|
+
reason: "open-failed"
|
|
83021
|
+
};
|
|
83022
|
+
}
|
|
83023
|
+
const launchedPid = launch.pid;
|
|
83024
|
+
if (launchedPid === null) {
|
|
83025
|
+
const failed = {
|
|
83026
|
+
profileName: options.profileName,
|
|
83027
|
+
profileKey: options.profileKey,
|
|
83028
|
+
profileHome: options.profileHome,
|
|
83029
|
+
appPath: candidate.appPath,
|
|
83030
|
+
bundleId: candidate.bundleId,
|
|
83031
|
+
pid: null,
|
|
83032
|
+
appServerPid: null,
|
|
83033
|
+
launchedAt: null,
|
|
83034
|
+
lastVerifiedAt: null,
|
|
83035
|
+
lastStatus: "failed",
|
|
83036
|
+
lastError: "launch-timeout"
|
|
83037
|
+
};
|
|
83038
|
+
await patchManagedInstance(failed);
|
|
83039
|
+
return {
|
|
83040
|
+
status: "failed",
|
|
83041
|
+
profileName: options.profileName,
|
|
83042
|
+
instance: managedInstanceToPayload(failed, false),
|
|
83043
|
+
reason: "launch-timeout"
|
|
83044
|
+
};
|
|
83045
|
+
}
|
|
83046
|
+
const appServerPid = await waitForAppServerPid(launchedPid, options.profileHome);
|
|
83047
|
+
if (appServerPid === null) {
|
|
83048
|
+
const failed = {
|
|
83049
|
+
profileName: options.profileName,
|
|
83050
|
+
profileKey: options.profileKey,
|
|
83051
|
+
profileHome: options.profileHome,
|
|
83052
|
+
appPath: candidate.appPath,
|
|
83053
|
+
bundleId: candidate.bundleId,
|
|
83054
|
+
pid: null,
|
|
83055
|
+
appServerPid: null,
|
|
83056
|
+
launchedAt: null,
|
|
83057
|
+
lastVerifiedAt: Date.now(),
|
|
83058
|
+
lastStatus: "failed",
|
|
83059
|
+
lastError: "app-server-not-verified"
|
|
83060
|
+
};
|
|
83061
|
+
await patchManagedInstance(failed);
|
|
83062
|
+
return {
|
|
83063
|
+
status: "failed",
|
|
83064
|
+
profileName: options.profileName,
|
|
83065
|
+
instance: managedInstanceToPayload(failed, false),
|
|
83066
|
+
reason: "app-server-not-verified"
|
|
83067
|
+
};
|
|
83068
|
+
}
|
|
83069
|
+
const now = Date.now();
|
|
83070
|
+
const instance = {
|
|
83071
|
+
profileName: options.profileName,
|
|
83072
|
+
profileKey: options.profileKey,
|
|
83073
|
+
profileHome: options.profileHome,
|
|
83074
|
+
appPath: candidate.appPath,
|
|
83075
|
+
bundleId: candidate.bundleId,
|
|
83076
|
+
pid: launchedPid,
|
|
83077
|
+
appServerPid,
|
|
83078
|
+
launchedAt: now,
|
|
83079
|
+
lastVerifiedAt: now,
|
|
83080
|
+
lastStatus: "started",
|
|
83081
|
+
lastError: null
|
|
83082
|
+
};
|
|
83083
|
+
await patchManagedInstance(instance);
|
|
83084
|
+
return {
|
|
83085
|
+
status: "started",
|
|
83086
|
+
profileName: options.profileName,
|
|
83087
|
+
instance: managedInstanceToPayload(instance, true, appServerPid),
|
|
83088
|
+
reason: null
|
|
83089
|
+
};
|
|
83090
|
+
});
|
|
83091
|
+
}
|
|
83092
|
+
function stopOfficialCodexProfileInstance(profileName) {
|
|
83093
|
+
return enqueueProfileAction(async () => {
|
|
83094
|
+
const existing = await readManagedInstance(profileName);
|
|
83095
|
+
if (!existing) return {
|
|
83096
|
+
status: "not-running",
|
|
83097
|
+
profileName,
|
|
83098
|
+
instance: null,
|
|
83099
|
+
reason: "not-managed"
|
|
83100
|
+
};
|
|
83101
|
+
const { candidate } = await resolveCodexAppTarget();
|
|
83102
|
+
if (!candidate) {
|
|
83103
|
+
const stopped = {
|
|
83104
|
+
...existing,
|
|
83105
|
+
pid: null,
|
|
83106
|
+
appServerPid: null,
|
|
83107
|
+
lastVerifiedAt: Date.now(),
|
|
83108
|
+
lastStatus: "not-running",
|
|
83109
|
+
lastError: "official-codex-app-not-found"
|
|
83110
|
+
};
|
|
83111
|
+
await patchManagedInstance(stopped);
|
|
83112
|
+
return {
|
|
83113
|
+
status: "not-running",
|
|
83114
|
+
profileName,
|
|
83115
|
+
instance: managedInstanceToPayload(stopped, false),
|
|
83116
|
+
reason: "official-codex-app-not-found"
|
|
83117
|
+
};
|
|
83118
|
+
}
|
|
83119
|
+
const rows = await readProcessRows();
|
|
83120
|
+
const runtime = await resolveInstanceRuntimeState({
|
|
83121
|
+
instance: existing,
|
|
83122
|
+
candidate,
|
|
83123
|
+
rows
|
|
83124
|
+
});
|
|
83125
|
+
if (!runtime.running || !existing.pid) {
|
|
83126
|
+
const stopped = {
|
|
83127
|
+
...existing,
|
|
83128
|
+
pid: null,
|
|
83129
|
+
appServerPid: null,
|
|
83130
|
+
lastVerifiedAt: Date.now(),
|
|
83131
|
+
lastStatus: "not-running",
|
|
83132
|
+
lastError: null
|
|
83133
|
+
};
|
|
83134
|
+
await patchManagedInstance(stopped);
|
|
83135
|
+
return {
|
|
83136
|
+
status: "not-running",
|
|
83137
|
+
profileName,
|
|
83138
|
+
instance: managedInstanceToPayload(stopped, false),
|
|
83139
|
+
reason: null
|
|
83140
|
+
};
|
|
83141
|
+
}
|
|
83142
|
+
const tree = [existing.pid, ...getDescendantPids(existing.pid, rows)].filter((pid, index, all) => all.indexOf(pid) === index).sort((a, b) => b - a);
|
|
83143
|
+
await signalPids(tree, "SIGTERM");
|
|
83144
|
+
if (!await waitForMainPidExit(existing.pid, 5e3)) {
|
|
83145
|
+
await signalPids(tree, "SIGKILL");
|
|
83146
|
+
await waitForMainPidExit(existing.pid, EXIT_WAIT_MS);
|
|
83147
|
+
}
|
|
83148
|
+
const stillRunning = (await readProcessRows()).some((row) => row.pid === existing.pid);
|
|
83149
|
+
const stopped = {
|
|
83150
|
+
...existing,
|
|
83151
|
+
pid: stillRunning ? existing.pid : null,
|
|
83152
|
+
appServerPid: stillRunning ? runtime.appServerPid : null,
|
|
83153
|
+
lastVerifiedAt: Date.now(),
|
|
83154
|
+
lastStatus: stillRunning ? "failed" : "stopped",
|
|
83155
|
+
lastError: stillRunning ? "stop-timeout" : null
|
|
83156
|
+
};
|
|
83157
|
+
await patchManagedInstance(stopped);
|
|
83158
|
+
return {
|
|
83159
|
+
status: stillRunning ? "failed" : "stopped",
|
|
83160
|
+
profileName,
|
|
83161
|
+
instance: managedInstanceToPayload(stopped, stillRunning, stopped.appServerPid, runtime.startedAt),
|
|
83162
|
+
reason: stillRunning ? "stop-timeout" : null
|
|
83163
|
+
};
|
|
83164
|
+
});
|
|
83165
|
+
}
|
|
83166
|
+
function focusOfficialCodexProfileInstance(profileName) {
|
|
83167
|
+
return enqueueProfileAction(async () => {
|
|
83168
|
+
const existing = await readManagedInstance(profileName);
|
|
83169
|
+
if (!existing) return {
|
|
83170
|
+
status: "not-running",
|
|
83171
|
+
profileName,
|
|
83172
|
+
instance: null,
|
|
83173
|
+
reason: "not-managed"
|
|
83174
|
+
};
|
|
83175
|
+
const { candidate } = await resolveCodexAppTarget();
|
|
83176
|
+
if (!candidate) return {
|
|
83177
|
+
status: "not-running",
|
|
83178
|
+
profileName,
|
|
83179
|
+
instance: managedInstanceToPayload(existing, false),
|
|
83180
|
+
reason: "official-codex-app-not-found"
|
|
83181
|
+
};
|
|
83182
|
+
const runtime = await resolveInstanceRuntimeState({
|
|
83183
|
+
instance: existing,
|
|
83184
|
+
candidate,
|
|
83185
|
+
rows: await readProcessRows()
|
|
83186
|
+
});
|
|
83187
|
+
if (!runtime.running || !existing.pid) return {
|
|
83188
|
+
status: "not-running",
|
|
83189
|
+
profileName,
|
|
83190
|
+
instance: managedInstanceToPayload(existing, false),
|
|
83191
|
+
reason: null
|
|
83192
|
+
};
|
|
83193
|
+
const focused = await focusMainPid(existing.pid);
|
|
83194
|
+
const verified = {
|
|
83195
|
+
...existing,
|
|
83196
|
+
appServerPid: runtime.appServerPid,
|
|
83197
|
+
lastVerifiedAt: Date.now(),
|
|
83198
|
+
lastStatus: focused ? "focused" : "focus-unknown",
|
|
83199
|
+
lastError: focused ? null : "focus-unknown"
|
|
83200
|
+
};
|
|
83201
|
+
await patchManagedInstance(verified);
|
|
83202
|
+
return {
|
|
83203
|
+
status: focused ? "focused" : "focus-unknown",
|
|
83204
|
+
profileName,
|
|
83205
|
+
instance: managedInstanceToPayload(verified, true, runtime.appServerPid, runtime.startedAt),
|
|
83206
|
+
reason: focused ? null : "focus-unknown"
|
|
83207
|
+
};
|
|
83208
|
+
});
|
|
83209
|
+
}
|
|
83210
|
+
function focusOfficialCodexObservedProfileInstance(profileName, pid, appServerPid) {
|
|
83211
|
+
return enqueueProfileAction(async () => {
|
|
83212
|
+
const { candidate } = await resolveCodexAppTarget();
|
|
83213
|
+
if (!candidate) return {
|
|
83214
|
+
status: "not-running",
|
|
83215
|
+
profileName,
|
|
83216
|
+
instance: null,
|
|
83217
|
+
reason: "official-codex-app-not-found"
|
|
83218
|
+
};
|
|
83219
|
+
const rows = await readProcessRows();
|
|
83220
|
+
const mainRow = rows.find((row) => row.pid === pid);
|
|
83221
|
+
if (!mainRow || !isMainProcessRow(mainRow, candidate)) return {
|
|
83222
|
+
status: "not-running",
|
|
83223
|
+
profileName,
|
|
83224
|
+
instance: null,
|
|
83225
|
+
reason: null
|
|
83226
|
+
};
|
|
83227
|
+
const runtimeAppServerPid = findAppServerPid(rows, pid) ?? appServerPid;
|
|
83228
|
+
const focused = await focusMainPid(pid);
|
|
83229
|
+
const instance = {
|
|
83230
|
+
profileName,
|
|
83231
|
+
profileKey: null,
|
|
83232
|
+
appPath: candidate.appPath,
|
|
83233
|
+
bundleId: candidate.bundleId,
|
|
83234
|
+
pid,
|
|
83235
|
+
appServerPid: runtimeAppServerPid,
|
|
83236
|
+
running: true,
|
|
83237
|
+
startedAt: toIso(mainRow.startedAt),
|
|
83238
|
+
uptimeMs: typeof mainRow.startedAt === "number" ? Math.max(0, Date.now() - mainRow.startedAt) : null,
|
|
83239
|
+
launchedAt: null,
|
|
83240
|
+
lastVerifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
83241
|
+
lastStatus: focused ? "focused" : "focus-unknown",
|
|
83242
|
+
lastError: focused ? null : "focus-unknown"
|
|
83243
|
+
};
|
|
83244
|
+
return {
|
|
83245
|
+
status: focused ? "focused" : "focus-unknown",
|
|
83246
|
+
profileName,
|
|
83247
|
+
instance,
|
|
83248
|
+
reason: focused ? null : "focus-unknown"
|
|
83249
|
+
};
|
|
83250
|
+
});
|
|
83251
|
+
}
|
|
83252
|
+
function stopOfficialCodexObservedProfileInstance(profileName, pid, appServerPid) {
|
|
83253
|
+
return enqueueProfileAction(async () => {
|
|
83254
|
+
const { candidate } = await resolveCodexAppTarget();
|
|
83255
|
+
if (!candidate) return {
|
|
83256
|
+
status: "not-running",
|
|
83257
|
+
profileName,
|
|
83258
|
+
instance: null,
|
|
83259
|
+
reason: "official-codex-app-not-found"
|
|
83260
|
+
};
|
|
83261
|
+
const rows = await readProcessRows();
|
|
83262
|
+
const mainRow = rows.find((row) => row.pid === pid);
|
|
83263
|
+
if (!mainRow || !isMainProcessRow(mainRow, candidate)) return {
|
|
83264
|
+
status: "not-running",
|
|
83265
|
+
profileName,
|
|
83266
|
+
instance: null,
|
|
83267
|
+
reason: null
|
|
83268
|
+
};
|
|
83269
|
+
const runtimeAppServerPid = findAppServerPid(rows, pid) ?? appServerPid;
|
|
83270
|
+
const tree = [pid, ...getDescendantPids(pid, rows)].filter((entry, index, all) => all.indexOf(entry) === index).sort((a, b) => b - a);
|
|
83271
|
+
await signalPids(tree, "SIGTERM");
|
|
83272
|
+
if (!await waitForMainPidExit(pid, 5e3)) {
|
|
83273
|
+
await signalPids(tree, "SIGKILL");
|
|
83274
|
+
await waitForMainPidExit(pid, EXIT_WAIT_MS);
|
|
83275
|
+
}
|
|
83276
|
+
const stillRunning = (await readProcessRows()).some((row) => row.pid === pid);
|
|
83277
|
+
const instance = {
|
|
83278
|
+
profileName,
|
|
83279
|
+
profileKey: null,
|
|
83280
|
+
appPath: candidate.appPath,
|
|
83281
|
+
bundleId: candidate.bundleId,
|
|
83282
|
+
pid: stillRunning ? pid : null,
|
|
83283
|
+
appServerPid: stillRunning ? runtimeAppServerPid : null,
|
|
83284
|
+
running: stillRunning,
|
|
83285
|
+
startedAt: stillRunning ? toIso(mainRow.startedAt) : null,
|
|
83286
|
+
uptimeMs: stillRunning && typeof mainRow.startedAt === "number" ? Math.max(0, Date.now() - mainRow.startedAt) : null,
|
|
83287
|
+
launchedAt: null,
|
|
83288
|
+
lastVerifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
83289
|
+
lastStatus: stillRunning ? "failed" : "stopped",
|
|
83290
|
+
lastError: stillRunning ? "stop-timeout" : null
|
|
83291
|
+
};
|
|
83292
|
+
return {
|
|
83293
|
+
status: stillRunning ? "failed" : "stopped",
|
|
83294
|
+
profileName,
|
|
83295
|
+
instance,
|
|
83296
|
+
reason: stillRunning ? "stop-timeout" : null
|
|
83297
|
+
};
|
|
83298
|
+
});
|
|
83299
|
+
}
|
|
83300
|
+
async function restartOfficialCodexProfileInstance(options) {
|
|
83301
|
+
const stopped = await stopOfficialCodexProfileInstance(options.profileName);
|
|
83302
|
+
if (stopped.status === "failed") return stopped;
|
|
83303
|
+
const launched = await launchOfficialCodexProfileInstance(options);
|
|
83304
|
+
return {
|
|
83305
|
+
...launched,
|
|
83306
|
+
status: launched.status === "started" ? "restarted" : launched.status
|
|
83307
|
+
};
|
|
83308
|
+
}
|
|
83309
|
+
function restartOfficialCodexApp(options = {}) {
|
|
82195
83310
|
if (restartPromise) return restartPromise;
|
|
82196
|
-
restartPromise = restartOfficialCodexAppOnce(
|
|
83311
|
+
restartPromise = restartOfficialCodexAppOnce({
|
|
83312
|
+
source: options.source ?? "manual",
|
|
83313
|
+
currentProfileKey: options.currentProfileKey ?? null,
|
|
83314
|
+
launchIfNotRunning: options.launchIfNotRunning ?? (options.source ?? "manual") === "manual"
|
|
83315
|
+
}).finally(() => {
|
|
82197
83316
|
restartPromise = null;
|
|
82198
83317
|
});
|
|
82199
83318
|
return restartPromise;
|
|
@@ -82304,6 +83423,37 @@ function describeActiveAuthOwner(snapshot) {
|
|
|
82304
83423
|
if (!snapshot) return null;
|
|
82305
83424
|
return snapshot.email ?? snapshot.userId ?? snapshot.chatgptUserId ?? snapshot.accountId ?? null;
|
|
82306
83425
|
}
|
|
83426
|
+
async function createIsolatedCodexLoginHome() {
|
|
83427
|
+
const home = await promises.mkdtemp(nodePath.join(nodeOs.tmpdir(), "codexuse-login-"));
|
|
83428
|
+
let cleaned = false;
|
|
83429
|
+
return {
|
|
83430
|
+
home,
|
|
83431
|
+
authPath: nodePath.join(home, "auth.json"),
|
|
83432
|
+
cleanup: async () => {
|
|
83433
|
+
if (cleaned) return;
|
|
83434
|
+
cleaned = true;
|
|
83435
|
+
await promises.rm(home, {
|
|
83436
|
+
recursive: true,
|
|
83437
|
+
force: true
|
|
83438
|
+
});
|
|
83439
|
+
}
|
|
83440
|
+
};
|
|
83441
|
+
}
|
|
83442
|
+
async function cleanupProfileAuthSession(session) {
|
|
83443
|
+
const tasks = [];
|
|
83444
|
+
if (session.restoreConfig) {
|
|
83445
|
+
const restore = session.restoreConfig;
|
|
83446
|
+
session.restoreConfig = void 0;
|
|
83447
|
+
tasks.push(Promise.resolve().then(restore));
|
|
83448
|
+
}
|
|
83449
|
+
if (session.cleanupAuthHome) {
|
|
83450
|
+
const cleanup = session.cleanupAuthHome;
|
|
83451
|
+
session.cleanupAuthHome = void 0;
|
|
83452
|
+
tasks.push(Promise.resolve().then(cleanup));
|
|
83453
|
+
}
|
|
83454
|
+
const results = await Promise.allSettled(tasks);
|
|
83455
|
+
for (const result of results) if (result.status === "rejected") console.warn("Failed to clean up profile auth session:", result.reason);
|
|
83456
|
+
}
|
|
82307
83457
|
async function resolveCodexBinaryOrThrow(context) {
|
|
82308
83458
|
try {
|
|
82309
83459
|
return await requireCodexCli();
|
|
@@ -82311,7 +83461,7 @@ async function resolveCodexBinaryOrThrow(context) {
|
|
|
82311
83461
|
if (error instanceof CodexCliMissingError) {
|
|
82312
83462
|
const reason = error.status.reason ?? "Codex CLI not found. Install @openai/codex or set CODEX_BINARY.";
|
|
82313
83463
|
const message = context ? `${context} ${reason}` : reason;
|
|
82314
|
-
throw new Error(message);
|
|
83464
|
+
throw new Error(message, { cause: error });
|
|
82315
83465
|
}
|
|
82316
83466
|
throw error;
|
|
82317
83467
|
}
|
|
@@ -83018,6 +84168,61 @@ const createServer = fn(function* () {
|
|
|
83018
84168
|
properties
|
|
83019
84169
|
});
|
|
83020
84170
|
};
|
|
84171
|
+
const lowRemainingNotifications = /* @__PURE__ */ new Map();
|
|
84172
|
+
const finitePercent = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.min(100, value)) : null;
|
|
84173
|
+
const formatLowRemainingPercent = (value) => {
|
|
84174
|
+
const rounded = Math.round(value * 10) / 10;
|
|
84175
|
+
return (Number.isInteger(rounded) ? rounded.toFixed(0) : rounded.toFixed(1)).replace(/\.0$/, "");
|
|
84176
|
+
};
|
|
84177
|
+
const buildRateLimitResetKey = (window) => {
|
|
84178
|
+
if (typeof window.resetsAt === "string" && window.resetsAt.trim()) return `at:${window.resetsAt.trim()}`;
|
|
84179
|
+
if (typeof window.windowMinutes === "number" && Number.isFinite(window.windowMinutes)) return `window:${window.windowMinutes}`;
|
|
84180
|
+
return "unknown";
|
|
84181
|
+
};
|
|
84182
|
+
const maybeSendLowRemainingNotification = async (payload) => {
|
|
84183
|
+
const settings = normalizeAutoRollSettings(await getStoredAutoRollSettings());
|
|
84184
|
+
if (!settings.lowRemainingNotificationEnabled) return;
|
|
84185
|
+
if (!payload.snapshot) return;
|
|
84186
|
+
const windows = [{
|
|
84187
|
+
key: "primary",
|
|
84188
|
+
window: payload.snapshot.primary
|
|
84189
|
+
}, {
|
|
84190
|
+
key: "secondary",
|
|
84191
|
+
window: payload.snapshot.secondary
|
|
84192
|
+
}];
|
|
84193
|
+
for (const entry of windows) {
|
|
84194
|
+
if (!entry.window) continue;
|
|
84195
|
+
const usedPercent = finitePercent(entry.window.usedPercent);
|
|
84196
|
+
if (usedPercent === null) continue;
|
|
84197
|
+
const remainingPercent = Math.max(0, 100 - usedPercent);
|
|
84198
|
+
const stateKey = `${payload.accountId}:${entry.key}`;
|
|
84199
|
+
if (remainingPercent > settings.lowRemainingNotificationThreshold) {
|
|
84200
|
+
lowRemainingNotifications.delete(stateKey);
|
|
84201
|
+
continue;
|
|
84202
|
+
}
|
|
84203
|
+
const resetKey = buildRateLimitResetKey(entry.window);
|
|
84204
|
+
const previous = lowRemainingNotifications.get(stateKey);
|
|
84205
|
+
if (previous && previous.resetKey === resetKey && previous.threshold === settings.lowRemainingNotificationThreshold) continue;
|
|
84206
|
+
const remainingText = formatLowRemainingPercent(remainingPercent);
|
|
84207
|
+
const switchText = settings.enabled ? `Auto-roll will switch at or below ${settings.switchRemainingThreshold}% left.` : "Auto-roll is currently off.";
|
|
84208
|
+
sendDesktopParentMessage$1({
|
|
84209
|
+
type: "t3-server:show-notification",
|
|
84210
|
+
title: "Codex usage low",
|
|
84211
|
+
body: `${payload.profileName} has ${remainingText}% left. ${switchText}`
|
|
84212
|
+
});
|
|
84213
|
+
lowRemainingNotifications.set(stateKey, {
|
|
84214
|
+
resetKey,
|
|
84215
|
+
threshold: settings.lowRemainingNotificationThreshold
|
|
84216
|
+
});
|
|
84217
|
+
trackAnalyticsEvent("low_remaining_notification_sent", {
|
|
84218
|
+
profileName: payload.profileName,
|
|
84219
|
+
accountId: payload.accountId,
|
|
84220
|
+
windowKey: entry.key,
|
|
84221
|
+
remainingPercent,
|
|
84222
|
+
threshold: settings.lowRemainingNotificationThreshold
|
|
84223
|
+
});
|
|
84224
|
+
}
|
|
84225
|
+
};
|
|
83021
84226
|
const publishProfileSwitched = (payload) => {
|
|
83022
84227
|
refreshTray();
|
|
83023
84228
|
return runPromise$2(pushBus.publishAll(WS_CHANNELS.profilesSwitched, payload));
|
|
@@ -83025,6 +84230,7 @@ const createServer = fn(function* () {
|
|
|
83025
84230
|
const publishCliStatusUpdated = (payload) => runPromise$2(pushBus.publishAll(WS_CHANNELS.cliStatusUpdated, payload));
|
|
83026
84231
|
let backgroundAutoRollInFlight = false;
|
|
83027
84232
|
let lastAutoRollAttempt = null;
|
|
84233
|
+
const autoRollRearmBlockedProfiles = /* @__PURE__ */ new Set();
|
|
83028
84234
|
const rememberAutoRollAttempt = (source, target) => {
|
|
83029
84235
|
if (!source || !target) return;
|
|
83030
84236
|
lastAutoRollAttempt = {
|
|
@@ -83037,29 +84243,177 @@ const createServer = fn(function* () {
|
|
|
83037
84243
|
if (!lastAutoRollAttempt) return false;
|
|
83038
84244
|
return lastAutoRollAttempt.source === source && lastAutoRollAttempt.target === target && Date.now() - lastAutoRollAttempt.timestamp < 3e4;
|
|
83039
84245
|
};
|
|
83040
|
-
const
|
|
83041
|
-
|
|
83042
|
-
|
|
83043
|
-
|
|
84246
|
+
const blockAutoRollUntilRearm = (profileName) => {
|
|
84247
|
+
if (!profileName) return;
|
|
84248
|
+
autoRollRearmBlockedProfiles.add(profileName);
|
|
84249
|
+
};
|
|
84250
|
+
const unblockAutoRollRearm = (profileName) => {
|
|
84251
|
+
if (!profileName) return;
|
|
84252
|
+
autoRollRearmBlockedProfiles.delete(profileName);
|
|
84253
|
+
};
|
|
84254
|
+
const isAutoRollBlockedUntilRearm = (profileName, summary, rearmRemainingThreshold) => {
|
|
84255
|
+
if (!autoRollRearmBlockedProfiles.has(profileName)) return false;
|
|
84256
|
+
if (summary.hasUsage && summary.remainingPercent > rearmRemainingThreshold) {
|
|
84257
|
+
autoRollRearmBlockedProfiles.delete(profileName);
|
|
84258
|
+
return false;
|
|
84259
|
+
}
|
|
84260
|
+
return true;
|
|
84261
|
+
};
|
|
84262
|
+
const rearmAutoRollBlockedProfiles = (usageSummaries, rearmRemainingThreshold) => {
|
|
84263
|
+
for (const profileName of Array.from(autoRollRearmBlockedProfiles)) {
|
|
84264
|
+
const summary = usageSummaries.get(profileName);
|
|
84265
|
+
if (!summary || !summary.hasUsage || summary.remainingPercent > rearmRemainingThreshold) autoRollRearmBlockedProfiles.delete(profileName);
|
|
84266
|
+
}
|
|
84267
|
+
};
|
|
84268
|
+
const resolveProfileIdentityKeyByName = async (profileName) => {
|
|
84269
|
+
if (!profileName) return null;
|
|
84270
|
+
const profile = (await profileManager.listProfiles()).find((entry) => entry.name === profileName);
|
|
84271
|
+
return profile ? resolveProfileIdentityKeyFromProfile(profile) : null;
|
|
84272
|
+
};
|
|
84273
|
+
const prepareOfficialCodexProfileLaunch = async (profileName) => {
|
|
84274
|
+
const profile = (await profileManager.listProfiles()).find((entry) => entry.name === profileName);
|
|
84275
|
+
if (!profile) throw new Error(`Profile '${profileName}' was not found.`);
|
|
84276
|
+
if (!profile.isValid || profile.tokenStatus?.requiresUserAction) throw new Error(`Profile '${profileName}' needs auth refresh before launch.`);
|
|
84277
|
+
const runtime = await profileManager.prepareProfileRuntime(profileName);
|
|
84278
|
+
return {
|
|
84279
|
+
profileName,
|
|
84280
|
+
profileKey: resolveProfileIdentityKeyFromProfile(profile),
|
|
84281
|
+
profileHome: runtime.profileHome
|
|
84282
|
+
};
|
|
84283
|
+
};
|
|
84284
|
+
const resolveOfficialCodexProfileAccess = async () => {
|
|
84285
|
+
const visible = await loadLicenseVisibleProfiles(profileManager, { freshLicense: true });
|
|
84286
|
+
return {
|
|
84287
|
+
allProfileNames: new Set(visible.allProfiles.map((profile) => profile.name)),
|
|
84288
|
+
allowedProfileNames: new Set(visible.profiles.map((profile) => profile.name))
|
|
84289
|
+
};
|
|
84290
|
+
};
|
|
84291
|
+
const assertOfficialCodexProfileAccess = (profileName, access) => {
|
|
84292
|
+
if (!access.allProfileNames.has(profileName) || access.allowedProfileNames.has(profileName)) return;
|
|
84293
|
+
throw new Error("CodexUse Free supports Codex Apps for the 2 visible profiles. Upgrade to CodexUse Pro for unlimited profile windows.");
|
|
84294
|
+
};
|
|
84295
|
+
const toOfficialCodexProfileAccessRouteError = (error) => new RouteRequestError({ message: formatUserFacingError(error, { fallback: "Could not verify Codex App profile access." }) });
|
|
84296
|
+
const mapDefaultCodexAuthToProfile = async () => {
|
|
84297
|
+
const [snapshot, profiles] = await Promise.all([profileManager.captureActiveAuthSnapshot(), profileManager.listProfiles()]);
|
|
84298
|
+
const candidateKeys = new Set([
|
|
84299
|
+
resolveProfileIdentityKey({
|
|
84300
|
+
email: snapshot.email,
|
|
84301
|
+
accountId: snapshot.accountId,
|
|
84302
|
+
workspaceId: snapshot.workspaceId,
|
|
84303
|
+
metadata: {
|
|
84304
|
+
userId: snapshot.userId ?? void 0,
|
|
84305
|
+
chatgptUserId: snapshot.chatgptUserId ?? void 0,
|
|
84306
|
+
chatgptAccountUserId: snapshot.chatgptAccountUserId ?? void 0
|
|
84307
|
+
}
|
|
84308
|
+
}),
|
|
84309
|
+
snapshot.accountId,
|
|
84310
|
+
snapshot.email && snapshot.workspaceId ? resolveProfileIdentityKey({
|
|
84311
|
+
email: snapshot.email,
|
|
84312
|
+
workspaceId: snapshot.workspaceId
|
|
84313
|
+
}) : null,
|
|
84314
|
+
snapshot.email ? resolveProfileIdentityKey({ email: snapshot.email }) : null
|
|
84315
|
+
].filter((key) => Boolean(key)));
|
|
84316
|
+
if (candidateKeys.size === 0) return null;
|
|
84317
|
+
for (const profile of profiles) {
|
|
84318
|
+
const matchedKey = [
|
|
84319
|
+
resolveProfileIdentityKeyFromProfile(profile),
|
|
84320
|
+
resolveLegacyProfileAccountKey(profile),
|
|
84321
|
+
profile.accountId ?? null,
|
|
84322
|
+
profile.email ? resolveProfileIdentityKey({ email: profile.email }) : null,
|
|
84323
|
+
profile.email && profile.workspaceId ? resolveProfileIdentityKey({
|
|
84324
|
+
email: profile.email,
|
|
84325
|
+
workspaceId: profile.workspaceId
|
|
84326
|
+
}) : null
|
|
84327
|
+
].filter((key) => Boolean(key)).find((key) => candidateKeys.has(key)) ?? null;
|
|
84328
|
+
if (matchedKey) return {
|
|
84329
|
+
name: profile.name,
|
|
84330
|
+
key: matchedKey
|
|
84331
|
+
};
|
|
84332
|
+
}
|
|
84333
|
+
return null;
|
|
84334
|
+
};
|
|
84335
|
+
const enrichOfficialCodexInstances = async (payload) => {
|
|
84336
|
+
if (payload.unmanaged.length === 0) return payload;
|
|
84337
|
+
const defaultProfile = await mapDefaultCodexAuthToProfile();
|
|
84338
|
+
if (!defaultProfile) return payload;
|
|
84339
|
+
return {
|
|
84340
|
+
...payload,
|
|
84341
|
+
unmanaged: payload.unmanaged.map((instance) => instance.profileName || !instance.appServerPid ? instance : {
|
|
84342
|
+
...instance,
|
|
84343
|
+
profileName: defaultProfile.name,
|
|
84344
|
+
profileKey: defaultProfile.key,
|
|
84345
|
+
profileMatchSource: "default-auth"
|
|
84346
|
+
})
|
|
84347
|
+
};
|
|
84348
|
+
};
|
|
84349
|
+
const findObservedOfficialCodexProfileInstance = async (profileName) => {
|
|
84350
|
+
const instances = await enrichOfficialCodexInstances(await getOfficialCodexProfileInstances());
|
|
84351
|
+
if (instances.instances.some((instance) => instance.profileName === profileName && instance.running)) return null;
|
|
84352
|
+
const observed = instances.unmanaged.find((instance) => instance.profileName === profileName) ?? null;
|
|
84353
|
+
return observed ? {
|
|
84354
|
+
...observed,
|
|
84355
|
+
appPath: instances.appPath,
|
|
84356
|
+
bundleId: instances.bundleId
|
|
84357
|
+
} : null;
|
|
84358
|
+
};
|
|
84359
|
+
const resolveAutoRollRearmActionAfterManualSwitch = async (profileName) => {
|
|
84360
|
+
const settings = normalizeAutoRollSettings(await getStoredAutoRollSettings());
|
|
84361
|
+
if (!settings.enabled) return "none";
|
|
84362
|
+
const profile = (await profileManager.listProfiles()).find((entry) => entry.name === profileName);
|
|
84363
|
+
if (!profile) return "none";
|
|
84364
|
+
const accountId = resolveProfileIdentityKeyFromProfile(profile);
|
|
84365
|
+
const summary = summarizeRateLimitSnapshot((await loadCachedRateLimitSnapshots([accountId])).get(accountId) ?? profile.rateLimit ?? null);
|
|
84366
|
+
if (!summary.hasUsage) return "unblock";
|
|
84367
|
+
return summary.remainingPercent <= settings.switchRemainingThreshold ? "block" : "unblock";
|
|
84368
|
+
};
|
|
84369
|
+
const maybeRestartOfficialCodexAfterAutoRoll = async (targetProfileName, targetProfileKey) => {
|
|
84370
|
+
try {
|
|
84371
|
+
const settings = normalizeAutoRollSettings(await getStoredAutoRollSettings());
|
|
84372
|
+
if (!settings.restartOfficialCodexOnAutoRoll) return;
|
|
84373
|
+
const result = await restartOfficialCodexApp({
|
|
84374
|
+
source: "auto-roll",
|
|
84375
|
+
currentProfileKey: targetProfileKey,
|
|
84376
|
+
launchIfNotRunning: settings.launchOfficialCodexWhenClosedOnAutoRoll
|
|
84377
|
+
});
|
|
83044
84378
|
trackAnalyticsEvent("official_codex_restart_after_auto_roll", {
|
|
83045
84379
|
status: result.status,
|
|
83046
84380
|
reason: result.status === "skipped" || result.status === "failed" ? result.reason : void 0,
|
|
83047
84381
|
targetProfileName
|
|
83048
84382
|
});
|
|
83049
84383
|
if (result.status === "failed") logger.warn("Official Codex restart after auto-roll failed", result);
|
|
83050
|
-
else if (result.status === "restarted") logger.info("Official Codex restarted after auto-roll", result);
|
|
83051
|
-
}
|
|
84384
|
+
else if (result.status === "restarted" || result.status === "started") logger.info("Official Codex restarted after auto-roll", result);
|
|
84385
|
+
} catch (error) {
|
|
83052
84386
|
logger.warn("Official Codex restart after auto-roll threw", { error });
|
|
83053
|
-
}
|
|
84387
|
+
}
|
|
83054
84388
|
};
|
|
83055
84389
|
const handleProfileSwitch = async (name, source = "app") => {
|
|
83056
84390
|
if (!name) throw new Error("Profile name is required");
|
|
83057
84391
|
const previousProfile = source === "auto-roll" ? (await profileManager.getCurrentProfile()).name ?? null : null;
|
|
83058
84392
|
await profileManager.switchToProfile(name);
|
|
84393
|
+
let targetProfileKey = null;
|
|
84394
|
+
try {
|
|
84395
|
+
targetProfileKey = await resolveProfileIdentityKeyByName(name);
|
|
84396
|
+
await recordOfficialCodexProfileSwitch(targetProfileKey);
|
|
84397
|
+
} catch (error) {
|
|
84398
|
+
logger.warn("Failed to record Official Codex profile switch", {
|
|
84399
|
+
error,
|
|
84400
|
+
profileName: name
|
|
84401
|
+
});
|
|
84402
|
+
}
|
|
83059
84403
|
if (source === "auto-roll") rememberAutoRollAttempt(previousProfile, name);
|
|
84404
|
+
else try {
|
|
84405
|
+
const rearmAction = await resolveAutoRollRearmActionAfterManualSwitch(name);
|
|
84406
|
+
if (rearmAction === "block") blockAutoRollUntilRearm(name);
|
|
84407
|
+
else if (rearmAction === "unblock") unblockAutoRollRearm(name);
|
|
84408
|
+
} catch (error) {
|
|
84409
|
+
logger.warn("Failed to evaluate auto-roll rearm block after manual switch", {
|
|
84410
|
+
error,
|
|
84411
|
+
profileName: name
|
|
84412
|
+
});
|
|
84413
|
+
}
|
|
83060
84414
|
await publishProfileSwitched({ name });
|
|
83061
84415
|
trackAnalyticsEvent("profile_switched", { source });
|
|
83062
|
-
if (source === "auto-roll") maybeRestartOfficialCodexAfterAutoRoll(name);
|
|
84416
|
+
if (source === "auto-roll") await maybeRestartOfficialCodexAfterAutoRoll(name, targetProfileKey);
|
|
83063
84417
|
};
|
|
83064
84418
|
const maybeRunBackgroundAutoRoll = async () => {
|
|
83065
84419
|
if (backgroundAutoRollInFlight) return;
|
|
@@ -83083,14 +84437,17 @@ const createServer = fn(function* () {
|
|
|
83083
84437
|
rateLimit: snapshot
|
|
83084
84438
|
};
|
|
83085
84439
|
});
|
|
84440
|
+
rearmAutoRollBlockedProfiles(usageSummaries, settings.rearmRemainingThreshold);
|
|
83086
84441
|
const currentProfile = profilesWithSnapshots.find((profile) => profile.name === currentProfileName);
|
|
83087
84442
|
if (!currentProfile) return;
|
|
83088
84443
|
const currentSummary = usageSummaries.get(currentProfileName) ?? summarizeRateLimitSnapshot(currentProfile.rateLimit ?? null);
|
|
83089
|
-
if (!currentSummary.hasUsage
|
|
83090
|
-
|
|
84444
|
+
if (!currentSummary.hasUsage) return;
|
|
84445
|
+
if (isAutoRollBlockedUntilRearm(currentProfileName, currentSummary, settings.rearmRemainingThreshold)) return;
|
|
84446
|
+
if (currentSummary.remainingPercent > settings.switchRemainingThreshold) return;
|
|
84447
|
+
const candidate = computeAutoRollCandidate(profilesWithSnapshots, usageSummaries, currentProfileName, currentSummary, settings.switchRemainingThreshold, settings.priorityOrder, autoRollRearmBlockedProfiles);
|
|
83091
84448
|
if (!candidate || isRecentAutoRollAttempt(currentProfileName, candidate.profile.name)) return;
|
|
83092
|
-
rememberAutoRollAttempt(currentProfileName, candidate.profile.name);
|
|
83093
84449
|
await handleProfileSwitch(candidate.profile.name, "auto-roll");
|
|
84450
|
+
blockAutoRollUntilRearm(currentProfileName);
|
|
83094
84451
|
} catch (error) {
|
|
83095
84452
|
logger.warn("Background auto-roll failed", { error });
|
|
83096
84453
|
} finally {
|
|
@@ -83123,6 +84480,9 @@ const createServer = fn(function* () {
|
|
|
83123
84480
|
profileName: payload.profileName,
|
|
83124
84481
|
snapshot: payload.snapshot ?? null
|
|
83125
84482
|
}));
|
|
84483
|
+
maybeSendLowRemainingNotification(payload).catch((error) => {
|
|
84484
|
+
logger.warn("Low remaining notification failed", { error });
|
|
84485
|
+
});
|
|
83126
84486
|
maybeRunBackgroundAutoRoll();
|
|
83127
84487
|
};
|
|
83128
84488
|
const handleRateLimitStateChanged = (payload) => {
|
|
@@ -83531,9 +84891,15 @@ const createServer = fn(function* () {
|
|
|
83531
84891
|
return yield* promise(() => telegramBridge.testToken(body.token));
|
|
83532
84892
|
}
|
|
83533
84893
|
case WS_METHODS.profilesFetch: {
|
|
83534
|
-
const data = yield* promise(() => loadProfilesViewData());
|
|
84894
|
+
const data = yield* promise(() => loadProfilesViewData({ autoImportActiveAuth: true }));
|
|
84895
|
+
const currentProfile = data.currentProfile ? data.profiles.find((profile) => profile.name === data.currentProfile) ?? null : null;
|
|
84896
|
+
const currentProfileKey = currentProfile ? resolveProfileIdentityKeyFromProfile(currentProfile) : null;
|
|
84897
|
+
const officialCodexStatus = yield* promise(() => getOfficialCodexSyncStatus(currentProfileKey));
|
|
83535
84898
|
refreshTray();
|
|
83536
|
-
return
|
|
84899
|
+
return {
|
|
84900
|
+
...data,
|
|
84901
|
+
officialCodexStatus
|
|
84902
|
+
};
|
|
83537
84903
|
}
|
|
83538
84904
|
case WS_METHODS.profilesStartAuth: {
|
|
83539
84905
|
const body = stripRequestTag(request.body);
|
|
@@ -83545,30 +84911,23 @@ const createServer = fn(function* () {
|
|
|
83545
84911
|
if (yield* promise(() => profileManager.profileExists(name))) return yield* new RouteRequestError({ message: `Profile '${name}' already exists! Choose a different name.` });
|
|
83546
84912
|
} else if (!(yield* promise(() => profileManager.profileExists(name)))) return yield* new RouteRequestError({ message: `Profile '${name}' not found!` });
|
|
83547
84913
|
const existingSession = authSessions.get(name);
|
|
83548
|
-
if (existingSession
|
|
83549
|
-
existingSession.process.kill();
|
|
84914
|
+
if (existingSession) {
|
|
84915
|
+
if (!existingSession.completed) existingSession.process.kill();
|
|
83550
84916
|
if (existingSession.timeout) clearTimeout(existingSession.timeout);
|
|
83551
|
-
|
|
84917
|
+
cleanupProfileAuthSession(existingSession);
|
|
83552
84918
|
authSessions.delete(name);
|
|
83553
84919
|
}
|
|
83554
84920
|
const startedAt = Date.now();
|
|
83555
|
-
const
|
|
83556
|
-
|
|
83557
|
-
const restoreConfig = async () => {
|
|
83558
|
-
if (!cleanupConfig) return;
|
|
83559
|
-
const restore = cleanupConfig;
|
|
83560
|
-
cleanupConfig = null;
|
|
83561
|
-
try {
|
|
83562
|
-
await restore();
|
|
83563
|
-
} catch (error) {
|
|
83564
|
-
console.warn("Failed to restore Codex config after login attempt:", error);
|
|
83565
|
-
}
|
|
83566
|
-
};
|
|
84921
|
+
const loginHome = yield* promise(() => createIsolatedCodexLoginHome());
|
|
84922
|
+
const initialAuthSnapshot = yield* promise(() => profileManager.captureAuthSnapshot(loginHome.authPath));
|
|
83567
84923
|
try {
|
|
83568
|
-
const storageOptions = yield* promise(() => resolveCodexHomeStorageOptions());
|
|
83569
|
-
cleanupConfig = yield* promise(() => sanitizeCodexConfigForCli(storageOptions));
|
|
83570
84924
|
const codexPath = yield* promise(() => resolveCodexBinaryOrThrow("Codex CLI is required to start authentication."));
|
|
83571
|
-
const loginEnv = buildCliEnv(codexPath, {
|
|
84925
|
+
const loginEnv = buildCliEnv(codexPath, {
|
|
84926
|
+
ELECTRON_RUN_AS_NODE: "1",
|
|
84927
|
+
HOME: loginHome.home,
|
|
84928
|
+
USERPROFILE: loginHome.home,
|
|
84929
|
+
CODEX_HOME: loginHome.home
|
|
84930
|
+
});
|
|
83572
84931
|
const command = buildCodexCommand$1(codexPath, ["login"], loginEnv);
|
|
83573
84932
|
const loginProcess = spawn(command.command, command.args, {
|
|
83574
84933
|
stdio: [
|
|
@@ -83588,7 +84947,8 @@ const createServer = fn(function* () {
|
|
|
83588
84947
|
mode,
|
|
83589
84948
|
startedAt,
|
|
83590
84949
|
initialAuthSnapshot,
|
|
83591
|
-
|
|
84950
|
+
authPath: loginHome.authPath,
|
|
84951
|
+
cleanupAuthHome: loginHome.cleanup
|
|
83592
84952
|
};
|
|
83593
84953
|
authSessions.set(name, session);
|
|
83594
84954
|
loginProcess.stdout?.on("data", (chunk) => {
|
|
@@ -83608,7 +84968,6 @@ const createServer = fn(function* () {
|
|
|
83608
84968
|
session.completed = true;
|
|
83609
84969
|
session.exitCode = code;
|
|
83610
84970
|
if (session.timeout) clearTimeout(session.timeout);
|
|
83611
|
-
restoreConfig();
|
|
83612
84971
|
});
|
|
83613
84972
|
loginProcess.on("error", (error) => {
|
|
83614
84973
|
session.error = compactErrorText(error instanceof Error ? error.message : String(error)) || "Authentication failed.";
|
|
@@ -83618,7 +84977,6 @@ const createServer = fn(function* () {
|
|
|
83618
84977
|
clearTimeout(session.timeout);
|
|
83619
84978
|
session.timeout = void 0;
|
|
83620
84979
|
}
|
|
83621
|
-
restoreConfig();
|
|
83622
84980
|
});
|
|
83623
84981
|
session.timeout = setTimeout(() => {
|
|
83624
84982
|
if (!session.completed) {
|
|
@@ -83632,7 +84990,7 @@ const createServer = fn(function* () {
|
|
|
83632
84990
|
return;
|
|
83633
84991
|
} catch (error) {
|
|
83634
84992
|
trackAnalyticsEvent("profile_auth_start_failed", { mode });
|
|
83635
|
-
yield* promise(() =>
|
|
84993
|
+
yield* promise(() => loginHome.cleanup());
|
|
83636
84994
|
throw error;
|
|
83637
84995
|
}
|
|
83638
84996
|
}
|
|
@@ -83657,38 +85015,41 @@ const createServer = fn(function* () {
|
|
|
83657
85015
|
const session = authSessions.get(body.name);
|
|
83658
85016
|
if (!session) return yield* new RouteRequestError({ message: "No authentication session found" });
|
|
83659
85017
|
if (!session.completed) return yield* new RouteRequestError({ message: "Authentication not yet completed" });
|
|
83660
|
-
|
|
83661
|
-
|
|
83662
|
-
|
|
83663
|
-
|
|
83664
|
-
|
|
83665
|
-
|
|
83666
|
-
const currentAuthSnapshot = yield* promise(() => profileManager.captureActiveAuthSnapshot());
|
|
83667
|
-
if (currentAuthSnapshot.fingerprint === (session.initialAuthSnapshot?.fingerprint ?? null)) {
|
|
83668
|
-
authSessions.delete(body.name);
|
|
85018
|
+
try {
|
|
85019
|
+
if (session.exitCode !== 0) {
|
|
85020
|
+
trackAnalyticsEvent("profile_auth_failed", { mode: session.mode });
|
|
85021
|
+
return yield* new RouteRequestError({ message: formatUserFacingError(session.error ?? `Authentication failed with code ${session.exitCode}`, { fallback: "Authentication failed. Try again." }) });
|
|
85022
|
+
}
|
|
85023
|
+
if (!session.authPath) {
|
|
83669
85024
|
trackAnalyticsEvent("profile_auth_complete_failed", { mode: session.mode });
|
|
83670
|
-
|
|
83671
|
-
|
|
83672
|
-
|
|
85025
|
+
return yield* new RouteRequestError({ message: "Authentication session is missing its isolated auth file." });
|
|
85026
|
+
}
|
|
85027
|
+
if (session.mode === "create") {
|
|
85028
|
+
const currentAuthSnapshot = yield* promise(() => profileManager.captureAuthSnapshot(session.authPath ?? void 0));
|
|
85029
|
+
if (currentAuthSnapshot.fingerprint === (session.initialAuthSnapshot?.fingerprint ?? null)) {
|
|
85030
|
+
trackAnalyticsEvent("profile_auth_complete_failed", { mode: session.mode });
|
|
85031
|
+
const activeOwner = describeActiveAuthOwner(currentAuthSnapshot);
|
|
85032
|
+
const ownerText = activeOwner ? ` Isolated auth still belongs to '${activeOwner}'.` : "";
|
|
85033
|
+
return yield* new RouteRequestError({ message: `Codex login finished without producing new auth for '${body.name}'.${ownerText} Finish the browser login for the new account and try again.` });
|
|
85034
|
+
}
|
|
83673
85035
|
}
|
|
83674
|
-
}
|
|
83675
|
-
try {
|
|
83676
85036
|
if (session.mode === "refresh") {
|
|
83677
|
-
yield* promise(() => profileManager.
|
|
85037
|
+
yield* promise(() => profileManager.refreshProfileAuthFromFile(body.name, session.authPath));
|
|
83678
85038
|
trackAnalyticsEvent("profile_tokens_refreshed", { mode: session.mode });
|
|
83679
85039
|
} else {
|
|
83680
85040
|
yield* promise(() => assertProfileCreationAllowed(profileManager));
|
|
83681
|
-
yield* promise(() => profileManager.
|
|
85041
|
+
yield* promise(() => profileManager.createProfileFromAuthFile(body.name, session.authPath));
|
|
83682
85042
|
trackAnalyticsEvent("profile_created", { mode: session.mode });
|
|
83683
85043
|
}
|
|
83684
85044
|
trackAnalyticsEvent("profile_auth_completed", { mode: session.mode });
|
|
83685
|
-
authSessions.delete(body.name);
|
|
83686
85045
|
refreshTray();
|
|
83687
85046
|
return;
|
|
83688
85047
|
} catch (error) {
|
|
83689
85048
|
trackAnalyticsEvent("profile_auth_complete_failed", { mode: session.mode });
|
|
83690
|
-
authSessions.delete(body.name);
|
|
83691
85049
|
throw error;
|
|
85050
|
+
} finally {
|
|
85051
|
+
authSessions.delete(body.name);
|
|
85052
|
+
yield* promise(() => cleanupProfileAuthSession(session));
|
|
83692
85053
|
}
|
|
83693
85054
|
}
|
|
83694
85055
|
case WS_METHODS.profilesSwitch: {
|
|
@@ -83712,13 +85073,39 @@ const createServer = fn(function* () {
|
|
|
83712
85073
|
}
|
|
83713
85074
|
case WS_METHODS.profilesDelete: {
|
|
83714
85075
|
const body = stripRequestTag(request.body);
|
|
83715
|
-
|
|
83716
|
-
yield*
|
|
83717
|
-
|
|
83718
|
-
const
|
|
85076
|
+
const name = body.name?.trim() ?? "";
|
|
85077
|
+
if (!name) return yield* new RouteRequestError({ message: "Profile name is required" });
|
|
85078
|
+
const replacementProfileName = typeof body.replacementProfileName === "string" && body.replacementProfileName.trim().length > 0 ? body.replacementProfileName.trim() : null;
|
|
85079
|
+
const removeActiveAuth = body.removeActiveAuth === true;
|
|
85080
|
+
if (replacementProfileName && removeActiveAuth) return yield* new RouteRequestError({ message: "Choose either a replacement profile or active auth removal." });
|
|
85081
|
+
if (replacementProfileName) {
|
|
85082
|
+
yield* promise(() => profileManager.deleteActiveProfileAndSwitch(name, replacementProfileName));
|
|
85083
|
+
let targetProfileKey = null;
|
|
85084
|
+
try {
|
|
85085
|
+
targetProfileKey = yield* promise(() => resolveProfileIdentityKeyByName(replacementProfileName));
|
|
85086
|
+
yield* promise(() => recordOfficialCodexProfileSwitch(targetProfileKey));
|
|
85087
|
+
} catch (error) {
|
|
85088
|
+
logger.warn("Failed to record Official Codex profile switch after deletion", {
|
|
85089
|
+
error,
|
|
85090
|
+
profileName: replacementProfileName
|
|
85091
|
+
});
|
|
85092
|
+
}
|
|
85093
|
+
yield* promise(() => publishProfileSwitched({ name: replacementProfileName }));
|
|
85094
|
+
trackAnalyticsEvent("profile_switched", { source: "app" });
|
|
85095
|
+
trackAnalyticsEvent("profile_deleted", { mode: "delete-and-switch" });
|
|
85096
|
+
refreshTray();
|
|
85097
|
+
return;
|
|
85098
|
+
}
|
|
85099
|
+
if (removeActiveAuth) {
|
|
85100
|
+
yield* promise(() => profileManager.deleteActiveProfileAndAuth(name));
|
|
85101
|
+
trackAnalyticsEvent("profile_deleted", { mode: "delete-and-sign-out" });
|
|
85102
|
+
refreshTray();
|
|
85103
|
+
return;
|
|
85104
|
+
}
|
|
85105
|
+
if ((yield* promise(() => profileManager.getCurrentProfile())).name === name) return yield* new RouteRequestError({ message: "This profile is the active Codex CLI login. Switch to another profile or confirm signing out before deleting it." });
|
|
85106
|
+
yield* promise(() => profileManager.deleteProfile(name));
|
|
85107
|
+
trackAnalyticsEvent("profile_deleted", { mode: "profile-only" });
|
|
83719
85108
|
refreshTray();
|
|
83720
|
-
const currentProfileName = typeof currentProfile.name === "string" && currentProfile.name.length > 0 ? currentProfile.name : null;
|
|
83721
|
-
if (currentProfileName) yield* promise(() => publishProfileSwitched({ name: currentProfileName }));
|
|
83722
85109
|
return;
|
|
83723
85110
|
}
|
|
83724
85111
|
case WS_METHODS.profilesExport: {
|
|
@@ -83739,12 +85126,12 @@ const createServer = fn(function* () {
|
|
|
83739
85126
|
clearTimeout(session.timeout);
|
|
83740
85127
|
session.timeout = void 0;
|
|
83741
85128
|
}
|
|
83742
|
-
|
|
85129
|
+
yield* promise(() => cleanupProfileAuthSession(session));
|
|
83743
85130
|
authSessions.delete(body.name);
|
|
83744
85131
|
return;
|
|
83745
85132
|
}
|
|
83746
85133
|
case WS_METHODS.rateLimitsRefreshNow: {
|
|
83747
|
-
const profiles = yield* promise(() => profileManager
|
|
85134
|
+
const { profiles } = yield* promise(() => loadLicenseVisibleProfiles(profileManager));
|
|
83748
85135
|
const profileNames = Array.from(new Set(profiles.filter((profile) => profile.isValid).map((profile) => profile.name).filter(Boolean)));
|
|
83749
85136
|
return { enqueued: (yield* promise(() => enqueueAccountsRefreshNow(profileNames))).enqueuedAccountIds.length };
|
|
83750
85137
|
}
|
|
@@ -83783,15 +85170,197 @@ const createServer = fn(function* () {
|
|
|
83783
85170
|
case WS_METHODS.autoRollSaveSettings: {
|
|
83784
85171
|
const normalized = normalizeAutoRollSettings(stripRequestTag(request.body).settings);
|
|
83785
85172
|
yield* promise(() => persistAutoRollSettings(normalized));
|
|
85173
|
+
maybeRunBackgroundAutoRoll();
|
|
83786
85174
|
trackAnalyticsEvent("auto_roll_settings_saved", {
|
|
83787
85175
|
enabled: normalized.enabled,
|
|
83788
|
-
|
|
83789
|
-
|
|
83790
|
-
restartOfficialCodexOnAutoRoll: normalized.restartOfficialCodexOnAutoRoll
|
|
85176
|
+
rearmRemainingThreshold: normalized.rearmRemainingThreshold,
|
|
85177
|
+
switchRemainingThreshold: normalized.switchRemainingThreshold,
|
|
85178
|
+
restartOfficialCodexOnAutoRoll: normalized.restartOfficialCodexOnAutoRoll,
|
|
85179
|
+
launchOfficialCodexWhenClosedOnAutoRoll: normalized.launchOfficialCodexWhenClosedOnAutoRoll,
|
|
85180
|
+
priorityCount: normalized.priorityOrder.length,
|
|
85181
|
+
lowRemainingNotificationEnabled: normalized.lowRemainingNotificationEnabled,
|
|
85182
|
+
lowRemainingNotificationThreshold: normalized.lowRemainingNotificationThreshold
|
|
83791
85183
|
});
|
|
83792
85184
|
return normalized;
|
|
83793
85185
|
}
|
|
83794
|
-
case WS_METHODS.officialCodexRestart: return yield* promise(() =>
|
|
85186
|
+
case WS_METHODS.officialCodexRestart: return yield* promise(async () => {
|
|
85187
|
+
return restartOfficialCodexApp({
|
|
85188
|
+
source: "manual",
|
|
85189
|
+
currentProfileKey: await resolveProfileIdentityKeyByName((await profileManager.getCurrentProfile()).name ?? null)
|
|
85190
|
+
});
|
|
85191
|
+
});
|
|
85192
|
+
case WS_METHODS.officialCodexInstances: return yield* promise(async () => enrichOfficialCodexInstances(await getOfficialCodexProfileInstances()));
|
|
85193
|
+
case WS_METHODS.officialCodexLaunchProfile: {
|
|
85194
|
+
const name = stripRequestTag(request.body).name?.trim() ?? "";
|
|
85195
|
+
const access = yield* tryPromise({
|
|
85196
|
+
try: () => resolveOfficialCodexProfileAccess(),
|
|
85197
|
+
catch: toOfficialCodexProfileAccessRouteError
|
|
85198
|
+
});
|
|
85199
|
+
yield* try_({
|
|
85200
|
+
try: () => assertOfficialCodexProfileAccess(name, access),
|
|
85201
|
+
catch: toOfficialCodexProfileAccessRouteError
|
|
85202
|
+
});
|
|
85203
|
+
const observed = yield* promise(() => findObservedOfficialCodexProfileInstance(name));
|
|
85204
|
+
if (observed) return {
|
|
85205
|
+
status: "already-running",
|
|
85206
|
+
profileName: name,
|
|
85207
|
+
instance: {
|
|
85208
|
+
profileName: name,
|
|
85209
|
+
profileKey: observed.profileKey ?? null,
|
|
85210
|
+
appPath: observed.appPath,
|
|
85211
|
+
bundleId: observed.bundleId,
|
|
85212
|
+
pid: observed.pid,
|
|
85213
|
+
appServerPid: observed.appServerPid,
|
|
85214
|
+
running: true,
|
|
85215
|
+
startedAt: observed.startedAt ?? null,
|
|
85216
|
+
uptimeMs: observed.uptimeMs ?? null,
|
|
85217
|
+
launchedAt: null,
|
|
85218
|
+
lastVerifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
85219
|
+
lastStatus: "running",
|
|
85220
|
+
lastError: null
|
|
85221
|
+
},
|
|
85222
|
+
reason: null
|
|
85223
|
+
};
|
|
85224
|
+
const options = yield* tryPromise({
|
|
85225
|
+
try: () => prepareOfficialCodexProfileLaunch(name),
|
|
85226
|
+
catch: (error) => new RouteRequestError({ message: formatUserFacingError(error, { fallback: "Could not prepare Codex App profile." }) })
|
|
85227
|
+
});
|
|
85228
|
+
trackAnalyticsEvent("official_codex_profile_launch_requested");
|
|
85229
|
+
return yield* promise(() => launchOfficialCodexProfileInstance(options));
|
|
85230
|
+
}
|
|
85231
|
+
case WS_METHODS.officialCodexFocusProfile: {
|
|
85232
|
+
const name = stripRequestTag(request.body).name?.trim() ?? "";
|
|
85233
|
+
const access = yield* tryPromise({
|
|
85234
|
+
try: () => resolveOfficialCodexProfileAccess(),
|
|
85235
|
+
catch: toOfficialCodexProfileAccessRouteError
|
|
85236
|
+
});
|
|
85237
|
+
yield* try_({
|
|
85238
|
+
try: () => assertOfficialCodexProfileAccess(name, access),
|
|
85239
|
+
catch: toOfficialCodexProfileAccessRouteError
|
|
85240
|
+
});
|
|
85241
|
+
return yield* promise(async () => {
|
|
85242
|
+
const observed = await findObservedOfficialCodexProfileInstance(name);
|
|
85243
|
+
if (observed) return focusOfficialCodexObservedProfileInstance(name, observed.pid, observed.appServerPid);
|
|
85244
|
+
return focusOfficialCodexProfileInstance(name);
|
|
85245
|
+
});
|
|
85246
|
+
}
|
|
85247
|
+
case WS_METHODS.officialCodexStopProfile: {
|
|
85248
|
+
const name = stripRequestTag(request.body).name?.trim() ?? "";
|
|
85249
|
+
const access = yield* tryPromise({
|
|
85250
|
+
try: () => resolveOfficialCodexProfileAccess(),
|
|
85251
|
+
catch: toOfficialCodexProfileAccessRouteError
|
|
85252
|
+
});
|
|
85253
|
+
yield* try_({
|
|
85254
|
+
try: () => assertOfficialCodexProfileAccess(name, access),
|
|
85255
|
+
catch: toOfficialCodexProfileAccessRouteError
|
|
85256
|
+
});
|
|
85257
|
+
return yield* promise(async () => {
|
|
85258
|
+
const observed = await findObservedOfficialCodexProfileInstance(name);
|
|
85259
|
+
if (observed) return stopOfficialCodexObservedProfileInstance(name, observed.pid, observed.appServerPid);
|
|
85260
|
+
return stopOfficialCodexProfileInstance(name);
|
|
85261
|
+
});
|
|
85262
|
+
}
|
|
85263
|
+
case WS_METHODS.officialCodexRestartProfile: {
|
|
85264
|
+
const name = stripRequestTag(request.body).name?.trim() ?? "";
|
|
85265
|
+
const access = yield* tryPromise({
|
|
85266
|
+
try: () => resolveOfficialCodexProfileAccess(),
|
|
85267
|
+
catch: toOfficialCodexProfileAccessRouteError
|
|
85268
|
+
});
|
|
85269
|
+
yield* try_({
|
|
85270
|
+
try: () => assertOfficialCodexProfileAccess(name, access),
|
|
85271
|
+
catch: toOfficialCodexProfileAccessRouteError
|
|
85272
|
+
});
|
|
85273
|
+
const options = yield* tryPromise({
|
|
85274
|
+
try: () => prepareOfficialCodexProfileLaunch(name),
|
|
85275
|
+
catch: (error) => new RouteRequestError({ message: formatUserFacingError(error, { fallback: "Could not prepare Codex App profile." }) })
|
|
85276
|
+
});
|
|
85277
|
+
trackAnalyticsEvent("official_codex_profile_restart_requested");
|
|
85278
|
+
return yield* promise(async () => {
|
|
85279
|
+
const observed = await findObservedOfficialCodexProfileInstance(name);
|
|
85280
|
+
if (observed) {
|
|
85281
|
+
const stopped = await stopOfficialCodexObservedProfileInstance(name, observed.pid, observed.appServerPid);
|
|
85282
|
+
if (stopped.status === "failed") return stopped;
|
|
85283
|
+
const launched = await launchOfficialCodexProfileInstance(options);
|
|
85284
|
+
return {
|
|
85285
|
+
...launched,
|
|
85286
|
+
status: launched.status === "started" ? "restarted" : launched.status
|
|
85287
|
+
};
|
|
85288
|
+
}
|
|
85289
|
+
return restartOfficialCodexProfileInstance(options);
|
|
85290
|
+
});
|
|
85291
|
+
}
|
|
85292
|
+
case WS_METHODS.officialCodexLaunchProfiles: {
|
|
85293
|
+
const body = stripRequestTag(request.body);
|
|
85294
|
+
const rawNames = Array.isArray(body.names) ? body.names : [];
|
|
85295
|
+
const names = Array.from(new Set(rawNames.map((name) => typeof name === "string" ? name.trim() : "").filter(Boolean)));
|
|
85296
|
+
const access = yield* tryPromise({
|
|
85297
|
+
try: () => resolveOfficialCodexProfileAccess(),
|
|
85298
|
+
catch: toOfficialCodexProfileAccessRouteError
|
|
85299
|
+
});
|
|
85300
|
+
yield* try_({
|
|
85301
|
+
try: () => {
|
|
85302
|
+
for (const name of names) assertOfficialCodexProfileAccess(name, access);
|
|
85303
|
+
},
|
|
85304
|
+
catch: toOfficialCodexProfileAccessRouteError
|
|
85305
|
+
});
|
|
85306
|
+
const results = [];
|
|
85307
|
+
for (const name of names) {
|
|
85308
|
+
const observed = yield* promise(() => findObservedOfficialCodexProfileInstance(name));
|
|
85309
|
+
if (observed) {
|
|
85310
|
+
results.push({
|
|
85311
|
+
status: "already-running",
|
|
85312
|
+
profileName: name,
|
|
85313
|
+
instance: {
|
|
85314
|
+
profileName: name,
|
|
85315
|
+
profileKey: observed.profileKey ?? null,
|
|
85316
|
+
appPath: observed.appPath,
|
|
85317
|
+
bundleId: observed.bundleId,
|
|
85318
|
+
pid: observed.pid,
|
|
85319
|
+
appServerPid: observed.appServerPid,
|
|
85320
|
+
running: true,
|
|
85321
|
+
startedAt: observed.startedAt ?? null,
|
|
85322
|
+
uptimeMs: observed.uptimeMs ?? null,
|
|
85323
|
+
launchedAt: null,
|
|
85324
|
+
lastVerifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
85325
|
+
lastStatus: "running",
|
|
85326
|
+
lastError: null
|
|
85327
|
+
},
|
|
85328
|
+
reason: null
|
|
85329
|
+
});
|
|
85330
|
+
continue;
|
|
85331
|
+
}
|
|
85332
|
+
const options = yield* tryPromise({
|
|
85333
|
+
try: () => prepareOfficialCodexProfileLaunch(name),
|
|
85334
|
+
catch: (error) => new RouteRequestError({ message: formatUserFacingError(error, { fallback: `Could not prepare Codex App profile '${name}'.` }) })
|
|
85335
|
+
});
|
|
85336
|
+
results.push(yield* promise(() => launchOfficialCodexProfileInstance(options)));
|
|
85337
|
+
}
|
|
85338
|
+
trackAnalyticsEvent("official_codex_profiles_launch_requested", { count: results.length });
|
|
85339
|
+
return { results };
|
|
85340
|
+
}
|
|
85341
|
+
case WS_METHODS.officialCodexStopProfiles: {
|
|
85342
|
+
const body = stripRequestTag(request.body);
|
|
85343
|
+
const rawNames = Array.isArray(body.names) ? body.names : [];
|
|
85344
|
+
const names = Array.from(new Set(rawNames.map((name) => typeof name === "string" ? name.trim() : "").filter(Boolean)));
|
|
85345
|
+
const access = yield* tryPromise({
|
|
85346
|
+
try: () => resolveOfficialCodexProfileAccess(),
|
|
85347
|
+
catch: toOfficialCodexProfileAccessRouteError
|
|
85348
|
+
});
|
|
85349
|
+
yield* try_({
|
|
85350
|
+
try: () => {
|
|
85351
|
+
for (const name of names) assertOfficialCodexProfileAccess(name, access);
|
|
85352
|
+
},
|
|
85353
|
+
catch: toOfficialCodexProfileAccessRouteError
|
|
85354
|
+
});
|
|
85355
|
+
const results = [];
|
|
85356
|
+
for (const name of names) results.push(yield* promise(async () => {
|
|
85357
|
+
const observed = await findObservedOfficialCodexProfileInstance(name);
|
|
85358
|
+
if (observed) return stopOfficialCodexObservedProfileInstance(name, observed.pid, observed.appServerPid);
|
|
85359
|
+
return stopOfficialCodexProfileInstance(name);
|
|
85360
|
+
}));
|
|
85361
|
+
trackAnalyticsEvent("official_codex_profiles_stop_requested", { count: results.length });
|
|
85362
|
+
return { results };
|
|
85363
|
+
}
|
|
83795
85364
|
case WS_METHODS.cliInfo: return yield* promise(() => getCodexCliInfo());
|
|
83796
85365
|
case WS_METHODS.cliStatus: return yield* promise(() => getCliInstallStatusWithFreshness());
|
|
83797
85366
|
case WS_METHODS.cliCheckFreshness: {
|
|
@@ -83871,8 +85440,8 @@ const ServerLive = succeed$3(Server, {
|
|
|
83871
85440
|
//#region src/serverLogger.ts
|
|
83872
85441
|
const ServerLoggerLive = gen(function* () {
|
|
83873
85442
|
const config = yield* ServerConfig$1;
|
|
83874
|
-
const logDir =
|
|
83875
|
-
const logPath =
|
|
85443
|
+
const logDir = nodePath.join(config.stateDir, "logs");
|
|
85444
|
+
const logPath = nodePath.join(logDir, "server.log");
|
|
83876
85445
|
yield* sync(() => {
|
|
83877
85446
|
fs.mkdirSync(logDir, { recursive: true });
|
|
83878
85447
|
});
|