codexuse-cli 3.9.1 → 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 +2544 -709
- 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";
|
|
@@ -46821,6 +46821,7 @@ const ThreadMetaUpdateCommand = Struct({
|
|
|
46821
46821
|
origin: optional$2(ThreadOrigin),
|
|
46822
46822
|
branch: optional$2(NullOr(TrimmedNonEmptyString)),
|
|
46823
46823
|
worktreePath: optional$2(NullOr(TrimmedNonEmptyString)),
|
|
46824
|
+
deletedAt: optional$2(NullOr(IsoDateTime)),
|
|
46824
46825
|
updatedAt: optional$2(IsoDateTime)
|
|
46825
46826
|
});
|
|
46826
46827
|
const ThreadRuntimeModeSetCommand = Struct({
|
|
@@ -47140,6 +47141,7 @@ const ThreadMetaUpdatedPayload$1 = Struct({
|
|
|
47140
47141
|
origin: optional$2(ThreadOrigin),
|
|
47141
47142
|
branch: optional$2(NullOr(TrimmedNonEmptyString)),
|
|
47142
47143
|
worktreePath: optional$2(NullOr(TrimmedNonEmptyString)),
|
|
47144
|
+
deletedAt: optional$2(NullOr(IsoDateTime)),
|
|
47143
47145
|
updatedAt: IsoDateTime
|
|
47144
47146
|
});
|
|
47145
47147
|
const ThreadRuntimeModeSetPayload$1 = Struct({
|
|
@@ -47395,8 +47397,27 @@ const ProjectionPendingApprovalDecision = NullOr(ProviderApprovalDecision);
|
|
|
47395
47397
|
const DispatchResult = Struct({ sequence: NonNegativeInt });
|
|
47396
47398
|
const OrchestrationGetSnapshotInput = Struct({});
|
|
47397
47399
|
const OrchestrationGetThreadSnapshotInput = Struct({ threadId: ThreadId });
|
|
47398
|
-
const OrchestrationSyncExternalThreadsInput = Struct({});
|
|
47399
|
-
const
|
|
47400
|
+
const OrchestrationSyncExternalThreadsInput = Struct({ projectId: optional$2(ProjectId) });
|
|
47401
|
+
const OrchestrationSyncExternalThreadsStats = Struct({
|
|
47402
|
+
bootstrappedProjectCount: NonNegativeInt,
|
|
47403
|
+
syncedProjectCount: NonNegativeInt,
|
|
47404
|
+
createdThreadCount: NonNegativeInt,
|
|
47405
|
+
restoredThreadCount: NonNegativeInt,
|
|
47406
|
+
foregroundSummaryCount: NonNegativeInt,
|
|
47407
|
+
foregroundHydratedThreadCount: NonNegativeInt,
|
|
47408
|
+
foregroundImportedMessageCount: NonNegativeInt,
|
|
47409
|
+
foregroundImportedActivityCount: NonNegativeInt,
|
|
47410
|
+
foregroundImportedPlanCount: NonNegativeInt,
|
|
47411
|
+
backgroundBackfillProjectCount: NonNegativeInt,
|
|
47412
|
+
parityWorkspaceCount: NonNegativeInt,
|
|
47413
|
+
hadErrors: Boolean$2
|
|
47414
|
+
});
|
|
47415
|
+
const OrchestrationSyncExternalThreadsResult = Struct({
|
|
47416
|
+
syncedAt: IsoDateTime,
|
|
47417
|
+
completed: Boolean$2,
|
|
47418
|
+
queued: Boolean$2,
|
|
47419
|
+
stats: optional$2(OrchestrationSyncExternalThreadsStats)
|
|
47420
|
+
});
|
|
47400
47421
|
const OrchestrationGetTurnDiffInput = TurnCountRange.mapFields(assign({ threadId: ThreadId }), { unsafePreserveChecks: true });
|
|
47401
47422
|
const OrchestrationGetTurnDiffResult = ThreadTurnDiff;
|
|
47402
47423
|
const OrchestrationGetFullThreadDiffInput = Struct({
|
|
@@ -47793,7 +47814,8 @@ const UserInputQuestion = Struct({
|
|
|
47793
47814
|
id: TrimmedNonEmptyStringSchema$1,
|
|
47794
47815
|
header: TrimmedNonEmptyStringSchema$1,
|
|
47795
47816
|
question: TrimmedNonEmptyStringSchema$1,
|
|
47796
|
-
options: Array$1(UserInputQuestionOption)
|
|
47817
|
+
options: Array$1(UserInputQuestionOption),
|
|
47818
|
+
multiSelect: optional$2(Boolean$2)
|
|
47797
47819
|
});
|
|
47798
47820
|
const UserInputRequestedPayload = Struct({ questions: Array$1(UserInputQuestion) });
|
|
47799
47821
|
const UserInputResolvedPayload = Struct({ answers: UnknownRecordSchema });
|
|
@@ -48742,6 +48764,13 @@ const WS_METHODS = {
|
|
|
48742
48764
|
autoRollGetSettings: "autoRoll.getSettings",
|
|
48743
48765
|
autoRollSaveSettings: "autoRoll.saveSettings",
|
|
48744
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",
|
|
48745
48774
|
cliInfo: "cli.info",
|
|
48746
48775
|
cliStatus: "cli.status",
|
|
48747
48776
|
cliCheckFreshness: "cli.checkFreshness",
|
|
@@ -48774,7 +48803,8 @@ const WS_CHANNELS = {
|
|
|
48774
48803
|
profilesSwitched: "profiles.switched",
|
|
48775
48804
|
rateLimitsUpdated: "rateLimits.updated",
|
|
48776
48805
|
rateLimitsState: "rateLimits.state",
|
|
48777
|
-
cliStatusUpdated: "cli.statusUpdated"
|
|
48806
|
+
cliStatusUpdated: "cli.statusUpdated",
|
|
48807
|
+
externalThreadSyncProgress: "externalThreadSync.progress"
|
|
48778
48808
|
};
|
|
48779
48809
|
const tagRequestBody = (tag$1, schema) => schema.mapFields(assign({ _tag: tag(tag$1) }), { unsafePreserveChecks: true });
|
|
48780
48810
|
const WebSocketRequestBody = Union([
|
|
@@ -48885,7 +48915,11 @@ const WebSocketRequestBody = Union([
|
|
|
48885
48915
|
accountKey: TrimmedNonEmptyString,
|
|
48886
48916
|
groupName: Union([TrimmedNonEmptyString, Null])
|
|
48887
48917
|
})),
|
|
48888
|
-
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
|
+
})),
|
|
48889
48923
|
tagRequestBody(WS_METHODS.profilesExport, Struct({ name: TrimmedNonEmptyString })),
|
|
48890
48924
|
tagRequestBody(WS_METHODS.profilesCancelAuth, Struct({ name: TrimmedNonEmptyString })),
|
|
48891
48925
|
tagRequestBody(WS_METHODS.rateLimitsRefreshNow, Struct({})),
|
|
@@ -48897,6 +48931,13 @@ const WebSocketRequestBody = Union([
|
|
|
48897
48931
|
tagRequestBody(WS_METHODS.autoRollGetSettings, Struct({})),
|
|
48898
48932
|
tagRequestBody(WS_METHODS.autoRollSaveSettings, Struct({ settings: Unknown })),
|
|
48899
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) })),
|
|
48900
48941
|
tagRequestBody(WS_METHODS.cliInfo, Struct({})),
|
|
48901
48942
|
tagRequestBody(WS_METHODS.cliStatus, Struct({})),
|
|
48902
48943
|
tagRequestBody(WS_METHODS.cliCheckFreshness, Struct({})),
|
|
@@ -48918,6 +48959,42 @@ const WsWelcomePayload = Struct({
|
|
|
48918
48959
|
bootstrapProjectId: optional$2(ProjectId),
|
|
48919
48960
|
bootstrapThreadId: optional$2(ThreadId)
|
|
48920
48961
|
});
|
|
48962
|
+
const ExternalThreadSyncProjectProgress = Struct({
|
|
48963
|
+
projectId: ProjectId,
|
|
48964
|
+
workspaceRoot: TrimmedNonEmptyString,
|
|
48965
|
+
title: TrimmedNonEmptyString,
|
|
48966
|
+
scannedThreads: NonNegativeInt,
|
|
48967
|
+
createdThreads: NonNegativeInt,
|
|
48968
|
+
restoredThreads: NonNegativeInt,
|
|
48969
|
+
hydratedThreads: NonNegativeInt,
|
|
48970
|
+
importedMessages: NonNegativeInt,
|
|
48971
|
+
importedActivities: NonNegativeInt,
|
|
48972
|
+
importedPlans: NonNegativeInt
|
|
48973
|
+
});
|
|
48974
|
+
const ExternalThreadSyncProgressPayload = Struct({
|
|
48975
|
+
status: Literals([
|
|
48976
|
+
"started",
|
|
48977
|
+
"project",
|
|
48978
|
+
"completed",
|
|
48979
|
+
"failed"
|
|
48980
|
+
]),
|
|
48981
|
+
runId: TrimmedNonEmptyString,
|
|
48982
|
+
startedAt: TrimmedNonEmptyString,
|
|
48983
|
+
updatedAt: TrimmedNonEmptyString,
|
|
48984
|
+
finishedAt: optional$2(TrimmedNonEmptyString),
|
|
48985
|
+
currentProjectIndex: NonNegativeInt,
|
|
48986
|
+
totalProjects: NonNegativeInt,
|
|
48987
|
+
project: optional$2(ExternalThreadSyncProjectProgress),
|
|
48988
|
+
projects: optional$2(Array$1(ExternalThreadSyncProjectProgress)),
|
|
48989
|
+
scannedThreads: NonNegativeInt,
|
|
48990
|
+
createdThreads: NonNegativeInt,
|
|
48991
|
+
restoredThreads: NonNegativeInt,
|
|
48992
|
+
hydratedThreads: NonNegativeInt,
|
|
48993
|
+
importedMessages: NonNegativeInt,
|
|
48994
|
+
importedActivities: NonNegativeInt,
|
|
48995
|
+
importedPlans: NonNegativeInt,
|
|
48996
|
+
hadErrors: Boolean$2
|
|
48997
|
+
});
|
|
48921
48998
|
const makeWsPushSchema = (channel, payload) => Struct({
|
|
48922
48999
|
type: Literal("push"),
|
|
48923
49000
|
sequence: WsPushSequence,
|
|
@@ -48934,6 +49011,7 @@ const WsPushProfilesSwitched = makeWsPushSchema(WS_CHANNELS.profilesSwitched, Un
|
|
|
48934
49011
|
const WsPushRateLimitsUpdated = makeWsPushSchema(WS_CHANNELS.rateLimitsUpdated, Unknown);
|
|
48935
49012
|
const WsPushRateLimitsState = makeWsPushSchema(WS_CHANNELS.rateLimitsState, Unknown);
|
|
48936
49013
|
const WsPushCliStatusUpdated = makeWsPushSchema(WS_CHANNELS.cliStatusUpdated, Unknown);
|
|
49014
|
+
const WsPushExternalThreadSyncProgress = makeWsPushSchema(WS_CHANNELS.externalThreadSyncProgress, ExternalThreadSyncProgressPayload);
|
|
48937
49015
|
const WsPushOrchestrationDomainEvent = makeWsPushSchema(ORCHESTRATION_WS_CHANNELS.domainEvent, OrchestrationEvent);
|
|
48938
49016
|
const WsPushChannelSchema = Literals([
|
|
48939
49017
|
WS_CHANNELS.gitActionProgress,
|
|
@@ -48946,6 +49024,7 @@ const WsPushChannelSchema = Literals([
|
|
|
48946
49024
|
WS_CHANNELS.rateLimitsUpdated,
|
|
48947
49025
|
WS_CHANNELS.rateLimitsState,
|
|
48948
49026
|
WS_CHANNELS.cliStatusUpdated,
|
|
49027
|
+
WS_CHANNELS.externalThreadSyncProgress,
|
|
48949
49028
|
ORCHESTRATION_WS_CHANNELS.domainEvent
|
|
48950
49029
|
]);
|
|
48951
49030
|
const WsPush = Union([
|
|
@@ -48959,6 +49038,7 @@ const WsPush = Union([
|
|
|
48959
49038
|
WsPushRateLimitsUpdated,
|
|
48960
49039
|
WsPushRateLimitsState,
|
|
48961
49040
|
WsPushCliStatusUpdated,
|
|
49041
|
+
WsPushExternalThreadSyncProgress,
|
|
48962
49042
|
WsPushOrchestrationDomainEvent
|
|
48963
49043
|
]);
|
|
48964
49044
|
const WsPushEnvelopeBase = Struct({
|
|
@@ -49135,7 +49215,7 @@ const launchDetached = (launch) => gen(function* () {
|
|
|
49135
49215
|
});
|
|
49136
49216
|
const make$9 = gen(function* () {
|
|
49137
49217
|
const open = yield* tryPromise({
|
|
49138
|
-
try: () => import("./open-
|
|
49218
|
+
try: () => import("./open-BWXrZXJl.mjs"),
|
|
49139
49219
|
catch: (cause) => new OpenError({
|
|
49140
49220
|
message: "failed to load browser opener",
|
|
49141
49221
|
cause
|
|
@@ -52736,17 +52816,24 @@ const decideOrchestrationCommand = fn("decideOrchestrationCommand")(function* ({
|
|
|
52736
52816
|
};
|
|
52737
52817
|
}
|
|
52738
52818
|
case "thread.meta.update": {
|
|
52739
|
-
yield* requireThread({
|
|
52819
|
+
const thread = yield* requireThread({
|
|
52740
52820
|
readModel,
|
|
52741
52821
|
command,
|
|
52742
52822
|
threadId: command.threadId
|
|
52743
52823
|
});
|
|
52744
|
-
if (command.projectId !== void 0)
|
|
52745
|
-
|
|
52746
|
-
|
|
52747
|
-
|
|
52748
|
-
|
|
52749
|
-
|
|
52824
|
+
if (command.projectId !== void 0) {
|
|
52825
|
+
yield* requireProject({
|
|
52826
|
+
readModel,
|
|
52827
|
+
command,
|
|
52828
|
+
projectId: command.projectId
|
|
52829
|
+
});
|
|
52830
|
+
if (!(thread.deletedAt !== null && command.deletedAt === null)) yield* requireThreadProject({
|
|
52831
|
+
readModel,
|
|
52832
|
+
command,
|
|
52833
|
+
threadId: command.threadId,
|
|
52834
|
+
projectId: command.projectId
|
|
52835
|
+
});
|
|
52836
|
+
}
|
|
52750
52837
|
const occurredAt = nowIso$2();
|
|
52751
52838
|
return {
|
|
52752
52839
|
...withEventBase({
|
|
@@ -52764,6 +52851,7 @@ const decideOrchestrationCommand = fn("decideOrchestrationCommand")(function* ({
|
|
|
52764
52851
|
...command.origin !== void 0 ? { origin: command.origin } : {},
|
|
52765
52852
|
...command.branch !== void 0 ? { branch: command.branch } : {},
|
|
52766
52853
|
...command.worktreePath !== void 0 ? { worktreePath: command.worktreePath } : {},
|
|
52854
|
+
...command.deletedAt !== void 0 ? { deletedAt: command.deletedAt } : {},
|
|
52767
52855
|
updatedAt: command.updatedAt ?? occurredAt
|
|
52768
52856
|
}
|
|
52769
52857
|
};
|
|
@@ -53369,6 +53457,7 @@ function projectEvent(model, event) {
|
|
|
53369
53457
|
} : {},
|
|
53370
53458
|
...payload.branch !== void 0 ? { branch: payload.branch } : {},
|
|
53371
53459
|
...payload.worktreePath !== void 0 ? { worktreePath: payload.worktreePath } : {},
|
|
53460
|
+
...payload.deletedAt !== void 0 ? { deletedAt: payload.deletedAt } : {},
|
|
53372
53461
|
updatedAt: payload.updatedAt
|
|
53373
53462
|
})
|
|
53374
53463
|
})));
|
|
@@ -54226,8 +54315,8 @@ const IGNORED_DIRECTORY_NAMES = new Set([
|
|
|
54226
54315
|
".cache"
|
|
54227
54316
|
]);
|
|
54228
54317
|
function expandHomePath(input) {
|
|
54229
|
-
if (input === "~") return
|
|
54230
|
-
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));
|
|
54231
54320
|
return input;
|
|
54232
54321
|
}
|
|
54233
54322
|
function isWindowsAbsolutePath(input) {
|
|
@@ -54236,13 +54325,13 @@ function isWindowsAbsolutePath(input) {
|
|
|
54236
54325
|
function resolveBrowseTarget(input) {
|
|
54237
54326
|
if (process.platform !== "win32" && isWindowsAbsolutePath(input.partialPath)) throw new Error("Windows-style paths are only supported on Windows.");
|
|
54238
54327
|
const expanded = expandHomePath(input.partialPath);
|
|
54239
|
-
if (
|
|
54240
|
-
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);
|
|
54241
54330
|
}
|
|
54242
54331
|
const workspaceIndexCache = /* @__PURE__ */ new Map();
|
|
54243
54332
|
const inFlightWorkspaceIndexBuilds = /* @__PURE__ */ new Map();
|
|
54244
54333
|
function toPosixPath(input) {
|
|
54245
|
-
return input.split(
|
|
54334
|
+
return input.split(nodePath.sep).join("/");
|
|
54246
54335
|
}
|
|
54247
54336
|
function parentPathOf(input) {
|
|
54248
54337
|
const separatorIndex = input.lastIndexOf("/");
|
|
@@ -54457,7 +54546,7 @@ async function buildWorkspaceIndex(cwd) {
|
|
|
54457
54546
|
const currentDirectories = pendingDirectories;
|
|
54458
54547
|
pendingDirectories = [];
|
|
54459
54548
|
const candidateEntriesByDirectory = (await mapWithConcurrency(currentDirectories, WORKSPACE_SCAN_READDIR_CONCURRENCY, async (relativeDir) => {
|
|
54460
|
-
const absoluteDir = relativeDir ?
|
|
54549
|
+
const absoluteDir = relativeDir ? nodePath.join(cwd, relativeDir) : cwd;
|
|
54461
54550
|
try {
|
|
54462
54551
|
return {
|
|
54463
54552
|
relativeDir,
|
|
@@ -54479,7 +54568,7 @@ async function buildWorkspaceIndex(cwd) {
|
|
|
54479
54568
|
if (!dirent.name || dirent.name === "." || dirent.name === "..") continue;
|
|
54480
54569
|
if (dirent.isDirectory() && IGNORED_DIRECTORY_NAMES.has(dirent.name)) continue;
|
|
54481
54570
|
if (!dirent.isDirectory() && !dirent.isFile()) continue;
|
|
54482
|
-
const relativePath = toPosixPath(relativeDir ?
|
|
54571
|
+
const relativePath = toPosixPath(relativeDir ? nodePath.join(relativeDir, dirent.name) : dirent.name);
|
|
54483
54572
|
if (isPathInIgnoredDirectory(relativePath)) continue;
|
|
54484
54573
|
candidates.push({
|
|
54485
54574
|
dirent,
|
|
@@ -54560,8 +54649,8 @@ async function searchWorkspaceEntries(input) {
|
|
|
54560
54649
|
async function browseFilesystemEntries(input) {
|
|
54561
54650
|
const resolvedInputPath = resolveBrowseTarget(input);
|
|
54562
54651
|
const endsWithSeparator = /[\\/]$/.test(input.partialPath) || input.partialPath === "~";
|
|
54563
|
-
const parentPath = endsWithSeparator ? resolvedInputPath :
|
|
54564
|
-
const prefix = endsWithSeparator ? "" :
|
|
54652
|
+
const parentPath = endsWithSeparator ? resolvedInputPath : nodePath.dirname(resolvedInputPath);
|
|
54653
|
+
const prefix = endsWithSeparator ? "" : nodePath.basename(resolvedInputPath);
|
|
54565
54654
|
const dirents = await fs$1.readdir(parentPath, { withFileTypes: true });
|
|
54566
54655
|
const showHidden = endsWithSeparator || prefix.startsWith(".");
|
|
54567
54656
|
const lowerPrefix = prefix.toLowerCase();
|
|
@@ -54569,7 +54658,7 @@ async function browseFilesystemEntries(input) {
|
|
|
54569
54658
|
parentPath,
|
|
54570
54659
|
entries: dirents.filter((dirent) => dirent.isDirectory() && dirent.name.toLowerCase().startsWith(lowerPrefix) && (showHidden || !dirent.name.startsWith("."))).map((dirent) => ({
|
|
54571
54660
|
name: dirent.name,
|
|
54572
|
-
fullPath:
|
|
54661
|
+
fullPath: nodePath.join(parentPath, dirent.name)
|
|
54573
54662
|
})).sort((left, right) => left.name.localeCompare(right.name))
|
|
54574
54663
|
};
|
|
54575
54664
|
}
|
|
@@ -55889,16 +55978,16 @@ const ProjectionPendingApprovalRepositoryLive = effect(ProjectionPendingApproval
|
|
|
55889
55978
|
//#region src/attachmentPaths.ts
|
|
55890
55979
|
const ATTACHMENTS_ROUTE_PREFIX = "/attachments";
|
|
55891
55980
|
function normalizeAttachmentRelativePath(rawRelativePath) {
|
|
55892
|
-
const normalized =
|
|
55981
|
+
const normalized = nodePath.normalize(rawRelativePath).replace(/^[/\\]+/, "");
|
|
55893
55982
|
if (normalized.length === 0 || normalized.startsWith("..") || normalized.includes("\0")) return null;
|
|
55894
55983
|
return normalized.replace(/\\/g, "/");
|
|
55895
55984
|
}
|
|
55896
55985
|
function resolveAttachmentRelativePath(input) {
|
|
55897
55986
|
const normalizedRelativePath = normalizeAttachmentRelativePath(input.relativePath);
|
|
55898
55987
|
if (!normalizedRelativePath) return null;
|
|
55899
|
-
const attachmentsRoot =
|
|
55900
|
-
const filePath =
|
|
55901
|
-
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;
|
|
55902
55991
|
return filePath;
|
|
55903
55992
|
}
|
|
55904
55993
|
//#endregion
|
|
@@ -57634,6 +57723,7 @@ const makeOrchestrationProjectionPipeline = gen(function* () {
|
|
|
57634
57723
|
...event.payload.origin !== void 0 ? { origin: event.payload.origin } : {},
|
|
57635
57724
|
...event.payload.branch !== void 0 ? { branch: event.payload.branch } : {},
|
|
57636
57725
|
...event.payload.worktreePath !== void 0 ? { worktreePath: event.payload.worktreePath } : {},
|
|
57726
|
+
...event.payload.deletedAt !== void 0 ? { deletedAt: event.payload.deletedAt } : {},
|
|
57637
57727
|
updatedAt: event.payload.updatedAt
|
|
57638
57728
|
});
|
|
57639
57729
|
return;
|
|
@@ -58131,6 +58221,75 @@ function normalizeCommitMessagePrompt(value) {
|
|
|
58131
58221
|
return normalized.length > 0 ? normalized : null;
|
|
58132
58222
|
}
|
|
58133
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
|
|
58134
58293
|
//#region ../../packages/runtime-app-state/src/storage/documents.ts
|
|
58135
58294
|
const APP_STORAGE_TABLE = "app_storage_documents";
|
|
58136
58295
|
const APP_STORAGE_DB_DIR = "t3-projects";
|
|
@@ -58214,7 +58373,7 @@ function ensureSchema(db, dbPath) {
|
|
|
58214
58373
|
initializedDbPaths.add(dbPath);
|
|
58215
58374
|
}
|
|
58216
58375
|
async function openDatabase(dbPath) {
|
|
58217
|
-
await promises.mkdir(
|
|
58376
|
+
await promises.mkdir(nodePath.dirname(dbPath), { recursive: true });
|
|
58218
58377
|
if (process.versions.bun !== void 0) {
|
|
58219
58378
|
const database = new (await (Function("return import('bun:sqlite')")())).Database(dbPath);
|
|
58220
58379
|
return {
|
|
@@ -58247,7 +58406,7 @@ async function withDatabase(dbPath, task) {
|
|
|
58247
58406
|
throw new Error("App storage database remained busy after retrying.");
|
|
58248
58407
|
}
|
|
58249
58408
|
function resolveAppStorageDbPath(userDataDir) {
|
|
58250
|
-
return
|
|
58409
|
+
return nodePath.join(userDataDir, APP_STORAGE_DB_DIR, APP_STORAGE_DB_NAME);
|
|
58251
58410
|
}
|
|
58252
58411
|
async function readDocument(dbPath, namespace, normalize) {
|
|
58253
58412
|
return withDatabase(dbPath, (db) => {
|
|
@@ -58317,21 +58476,21 @@ function clone(value) {
|
|
|
58317
58476
|
return JSON.parse(JSON.stringify(value));
|
|
58318
58477
|
}
|
|
58319
58478
|
function resolveDefaultUserDataDir() {
|
|
58320
|
-
const home = process.env.HOME || process.env.USERPROFILE ||
|
|
58479
|
+
const home = process.env.HOME || process.env.USERPROFILE || nodeOs.homedir();
|
|
58321
58480
|
if (!home) throw new Error("Unable to resolve home directory for app state.");
|
|
58322
|
-
if (process.platform === "darwin") return
|
|
58481
|
+
if (process.platform === "darwin") return nodePath.join(home, "Library", "Application Support", APP_NAME);
|
|
58323
58482
|
if (process.platform === "win32") {
|
|
58324
58483
|
const appData = process.env.APPDATA;
|
|
58325
|
-
if (appData) return
|
|
58326
|
-
return
|
|
58484
|
+
if (appData) return nodePath.join(appData, APP_NAME);
|
|
58485
|
+
return nodePath.join(home, "AppData", "Roaming", APP_NAME);
|
|
58327
58486
|
}
|
|
58328
|
-
return
|
|
58487
|
+
return nodePath.join(home, ".config", APP_NAME);
|
|
58329
58488
|
}
|
|
58330
58489
|
function getUserDataDir() {
|
|
58331
58490
|
return configuredUserDataDir ?? resolveDefaultUserDataDir();
|
|
58332
58491
|
}
|
|
58333
58492
|
function resolveLegacyAppStatePath() {
|
|
58334
|
-
return
|
|
58493
|
+
return nodePath.join(getUserDataDir(), LEGACY_APP_STATE_FILE);
|
|
58335
58494
|
}
|
|
58336
58495
|
function resolveStorageDbPath() {
|
|
58337
58496
|
return resolveAppStorageDbPath(getUserDataDir());
|
|
@@ -58341,9 +58500,23 @@ function createDefaultAppState() {
|
|
|
58341
58500
|
schemaVersion: 1,
|
|
58342
58501
|
autoRoll: {
|
|
58343
58502
|
enabled: false,
|
|
58344
|
-
|
|
58345
|
-
|
|
58346
|
-
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: {}
|
|
58347
58520
|
},
|
|
58348
58521
|
app: {
|
|
58349
58522
|
lastAppVersion: null,
|
|
@@ -58436,17 +58609,47 @@ function asString$9(value) {
|
|
|
58436
58609
|
const trimmed = value.trim();
|
|
58437
58610
|
return trimmed.length > 0 ? trimmed : null;
|
|
58438
58611
|
}
|
|
58612
|
+
function asNumberOrNull(value) {
|
|
58613
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
58614
|
+
}
|
|
58439
58615
|
function normalizeAppState(raw) {
|
|
58440
58616
|
const defaults = createDefaultAppState();
|
|
58441
58617
|
if (!isRecord$7(raw)) return defaults;
|
|
58618
|
+
const rawAutoRoll = isRecord$7(raw.autoRoll) ? raw.autoRoll : void 0;
|
|
58442
58619
|
const merged = clone(deepMerge(defaults, raw));
|
|
58443
58620
|
merged.schemaVersion = 1;
|
|
58444
|
-
|
|
58445
|
-
if (!
|
|
58446
|
-
|
|
58447
|
-
|
|
58448
|
-
|
|
58449
|
-
|
|
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
|
+
}
|
|
58450
58653
|
merged.app.lastAppVersion = asString$9(merged.app.lastAppVersion);
|
|
58451
58654
|
merged.app.pendingUpdateVersion = asString$9(merged.app.pendingUpdateVersion);
|
|
58452
58655
|
merged.app.lastProfileName = asString$9(merged.app.lastProfileName);
|
|
@@ -58678,13 +58881,13 @@ const IS_CASE_INSENSITIVE_PLATFORM$2 = process.platform === "darwin" || process.
|
|
|
58678
58881
|
function stripTrailingSeparators$2(input) {
|
|
58679
58882
|
const trimmed = input.trim();
|
|
58680
58883
|
if (trimmed.length === 0) return trimmed;
|
|
58681
|
-
const root =
|
|
58884
|
+
const root = nodePath.parse(trimmed).root;
|
|
58682
58885
|
let next = trimmed;
|
|
58683
58886
|
while (next.length > root.length && /[\\/]+$/.test(next)) next = next.slice(0, -1);
|
|
58684
58887
|
return next;
|
|
58685
58888
|
}
|
|
58686
58889
|
function canonicalizeWorkspaceRoot$1(input) {
|
|
58687
|
-
const resolved =
|
|
58890
|
+
const resolved = nodePath.resolve(input.trim());
|
|
58688
58891
|
return stripTrailingSeparators$2((() => {
|
|
58689
58892
|
try {
|
|
58690
58893
|
return realpathSync.native(resolved);
|
|
@@ -58698,10 +58901,10 @@ function workspaceRootKey$1(input) {
|
|
|
58698
58901
|
return IS_CASE_INSENSITIVE_PLATFORM$2 ? canonical.toLowerCase() : canonical;
|
|
58699
58902
|
}
|
|
58700
58903
|
function resolveAgentChatWorkspaceRoot() {
|
|
58701
|
-
return canonicalizeWorkspaceRoot$1(
|
|
58904
|
+
return canonicalizeWorkspaceRoot$1(nodePath.join(getUserDataDir(), AGENT_CHAT_WORKSPACE_DIRNAME));
|
|
58702
58905
|
}
|
|
58703
58906
|
function resolveAgentChatAgentsPath() {
|
|
58704
|
-
return
|
|
58907
|
+
return nodePath.join(resolveAgentChatWorkspaceRoot(), AGENT_CHAT_AGENTS_FILENAME);
|
|
58705
58908
|
}
|
|
58706
58909
|
function isAgentChatWorkspaceRoot(workspaceRoot) {
|
|
58707
58910
|
return workspaceRootKey$1(workspaceRoot) === workspaceRootKey$1(resolveAgentChatWorkspaceRoot());
|
|
@@ -58715,13 +58918,13 @@ const IS_CASE_INSENSITIVE_PLATFORM$1 = process.platform === "darwin" || process.
|
|
|
58715
58918
|
function stripTrailingSeparators$1(input) {
|
|
58716
58919
|
const trimmed = input.trim();
|
|
58717
58920
|
if (trimmed.length === 0) return trimmed;
|
|
58718
|
-
const root =
|
|
58921
|
+
const root = nodePath.parse(trimmed).root;
|
|
58719
58922
|
let next = trimmed;
|
|
58720
58923
|
while (next.length > root.length && /[\\/]+$/.test(next)) next = next.slice(0, -1);
|
|
58721
58924
|
return next;
|
|
58722
58925
|
}
|
|
58723
58926
|
function canonicalizeWorkspaceRoot(input) {
|
|
58724
|
-
const resolved =
|
|
58927
|
+
const resolved = nodePath.resolve(input.trim());
|
|
58725
58928
|
return stripTrailingSeparators$1((() => {
|
|
58726
58929
|
try {
|
|
58727
58930
|
return realpathSync.native(resolved);
|
|
@@ -58735,54 +58938,15 @@ function workspaceRootKey(input) {
|
|
|
58735
58938
|
return IS_CASE_INSENSITIVE_PLATFORM$1 ? canonical.toLowerCase() : canonical;
|
|
58736
58939
|
}
|
|
58737
58940
|
function resolveGeneralChatWorkspaceRoot() {
|
|
58738
|
-
return canonicalizeWorkspaceRoot(
|
|
58941
|
+
return canonicalizeWorkspaceRoot(nodePath.join(getUserDataDir(), GENERAL_CHAT_WORKSPACE_DIRNAME));
|
|
58739
58942
|
}
|
|
58740
58943
|
function resolveGeneralChatAgentsPath() {
|
|
58741
|
-
return
|
|
58944
|
+
return nodePath.join(resolveGeneralChatWorkspaceRoot(), GENERAL_CHAT_AGENTS_FILENAME);
|
|
58742
58945
|
}
|
|
58743
58946
|
function isGeneralChatWorkspaceRoot(workspaceRoot) {
|
|
58744
58947
|
return workspaceRootKey(workspaceRoot) === workspaceRootKey(resolveGeneralChatWorkspaceRoot());
|
|
58745
58948
|
}
|
|
58746
58949
|
//#endregion
|
|
58747
|
-
//#region ../../packages/contracts/src/settings/auto-roll.ts
|
|
58748
|
-
const DEFAULT_AUTO_ROLL_ENABLED = false;
|
|
58749
|
-
const DEFAULT_AUTO_ROLL_WARNING_THRESHOLD = 85;
|
|
58750
|
-
const DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD = 95;
|
|
58751
|
-
const DEFAULT_RESTART_OFFICIAL_CODEX_ON_AUTO_ROLL = false;
|
|
58752
|
-
const AUTO_ROLL_WARNING_MIN = 50;
|
|
58753
|
-
const AUTO_ROLL_WARNING_MAX = 99;
|
|
58754
|
-
const AUTO_ROLL_SWITCH_MAX = 100;
|
|
58755
|
-
function clampNumber(value, min, max) {
|
|
58756
|
-
return Math.min(max, Math.max(min, value));
|
|
58757
|
-
}
|
|
58758
|
-
function resolveFiniteNumber(value, fallback) {
|
|
58759
|
-
return Number.isFinite(value) ? value : fallback;
|
|
58760
|
-
}
|
|
58761
|
-
function sanitizeAutoRollWarningThreshold(value) {
|
|
58762
|
-
return clampNumber(resolveFiniteNumber(value, 85), 50, AUTO_ROLL_WARNING_MAX);
|
|
58763
|
-
}
|
|
58764
|
-
function sanitizeAutoRollSwitchThreshold(value, warningThreshold) {
|
|
58765
|
-
const sanitizedWarning = sanitizeAutoRollWarningThreshold(warningThreshold);
|
|
58766
|
-
return clampNumber(resolveFiniteNumber(value, 95), sanitizedWarning + 1, AUTO_ROLL_SWITCH_MAX);
|
|
58767
|
-
}
|
|
58768
|
-
function sanitizeAutoRollThresholds(warningThreshold, switchThreshold) {
|
|
58769
|
-
const sanitizedWarning = sanitizeAutoRollWarningThreshold(warningThreshold);
|
|
58770
|
-
return {
|
|
58771
|
-
warningThreshold: sanitizedWarning,
|
|
58772
|
-
switchThreshold: sanitizeAutoRollSwitchThreshold(switchThreshold, sanitizedWarning)
|
|
58773
|
-
};
|
|
58774
|
-
}
|
|
58775
|
-
function normalizeAutoRollSettings(raw) {
|
|
58776
|
-
const enabled = typeof raw?.enabled === "boolean" ? raw.enabled : false;
|
|
58777
|
-
const { warningThreshold: normalizedWarning, switchThreshold: normalizedSwitch } = sanitizeAutoRollThresholds(resolveFiniteNumber(typeof raw?.warningThreshold === "number" ? raw.warningThreshold : NaN, 85), resolveFiniteNumber(typeof raw?.switchThreshold === "number" ? raw.switchThreshold : NaN, 95));
|
|
58778
|
-
return {
|
|
58779
|
-
enabled,
|
|
58780
|
-
warningThreshold: normalizedWarning,
|
|
58781
|
-
switchThreshold: normalizedSwitch,
|
|
58782
|
-
restartOfficialCodexOnAutoRoll: raw?.restartOfficialCodexOnAutoRoll === true ? true : false
|
|
58783
|
-
};
|
|
58784
|
-
}
|
|
58785
|
-
//#endregion
|
|
58786
58950
|
//#region ../../packages/runtime-codex/src/codex/settings.ts
|
|
58787
58951
|
function asString$8(value) {
|
|
58788
58952
|
if (typeof value !== "string") return null;
|
|
@@ -58820,12 +58984,6 @@ function parseStoredLicense(raw) {
|
|
|
58820
58984
|
};
|
|
58821
58985
|
return Boolean(license.licenseKey || license.purchaseEmail || license.lastVerifiedAt || license.nextCheckAt || license.lastVerificationError || license.status) ? license : null;
|
|
58822
58986
|
}
|
|
58823
|
-
async function getLastProfileName() {
|
|
58824
|
-
return asString$8((await getAppState()).app.lastProfileName);
|
|
58825
|
-
}
|
|
58826
|
-
async function persistLastProfileName(profileName) {
|
|
58827
|
-
await patchAppState({ app: { lastProfileName: asString$8(profileName) } });
|
|
58828
|
-
}
|
|
58829
58987
|
async function getStoredLicense() {
|
|
58830
58988
|
return parseStoredLicense((await getAppState()).license);
|
|
58831
58989
|
}
|
|
@@ -58856,9 +59014,13 @@ async function persistAutoRollSettings(settings) {
|
|
|
58856
59014
|
const normalized = normalizeAutoRollSettings(settings ?? void 0);
|
|
58857
59015
|
await patchAppState({ autoRoll: {
|
|
58858
59016
|
enabled: normalized.enabled,
|
|
58859
|
-
|
|
58860
|
-
|
|
58861
|
-
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
|
|
58862
59024
|
} });
|
|
58863
59025
|
}
|
|
58864
59026
|
async function readCodexSettingsJsonRaw() {
|
|
@@ -58896,9 +59058,13 @@ async function writeCodexSettingsJsonRaw(payload) {
|
|
|
58896
59058
|
},
|
|
58897
59059
|
autoRoll: autoRoll ? {
|
|
58898
59060
|
enabled: autoRoll.enabled,
|
|
58899
|
-
|
|
58900
|
-
|
|
58901
|
-
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
|
|
58902
59068
|
} : void 0,
|
|
58903
59069
|
license: license ? {
|
|
58904
59070
|
licenseKey: license.licenseKey ?? null,
|
|
@@ -58936,7 +59102,7 @@ function normalizeSecret(value) {
|
|
|
58936
59102
|
return typeof value === "string" ? value.trim() : "";
|
|
58937
59103
|
}
|
|
58938
59104
|
async function readLegacyLicenseSecret() {
|
|
58939
|
-
const legacyPath =
|
|
59105
|
+
const legacyPath = nodePath.join(getUserDataDir(), LEGACY_LICENSE_SECRET_FILE$1);
|
|
58940
59106
|
try {
|
|
58941
59107
|
return normalizeSecret(await promises.readFile(legacyPath, "utf8"));
|
|
58942
59108
|
} catch (error) {
|
|
@@ -59285,13 +59451,13 @@ const IS_CASE_INSENSITIVE_PLATFORM = process.platform === "darwin" || process.pl
|
|
|
59285
59451
|
function stripTrailingSeparators(input) {
|
|
59286
59452
|
const resolved = input.trim();
|
|
59287
59453
|
if (resolved.length === 0) return resolved;
|
|
59288
|
-
const root =
|
|
59454
|
+
const root = nodePath.parse(resolved).root;
|
|
59289
59455
|
let next = resolved;
|
|
59290
59456
|
while (next.length > root.length && /[\\/]+$/.test(next)) next = next.slice(0, -1);
|
|
59291
59457
|
return next;
|
|
59292
59458
|
}
|
|
59293
59459
|
function canonicalizeProjectRoot(input) {
|
|
59294
|
-
const resolved =
|
|
59460
|
+
const resolved = nodePath.resolve(input.trim());
|
|
59295
59461
|
return stripTrailingSeparators((() => {
|
|
59296
59462
|
try {
|
|
59297
59463
|
return realpathSync.native(resolved);
|
|
@@ -59472,14 +59638,14 @@ let cachedStatus = null;
|
|
|
59472
59638
|
function getPathHintEntries() {
|
|
59473
59639
|
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
59474
59640
|
const homeEntries = homeDir ? [
|
|
59475
|
-
|
|
59476
|
-
|
|
59477
|
-
|
|
59478
|
-
|
|
59479
|
-
|
|
59480
|
-
|
|
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")
|
|
59481
59647
|
] : [];
|
|
59482
|
-
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);
|
|
59483
59649
|
return [
|
|
59484
59650
|
"/opt/homebrew/bin",
|
|
59485
59651
|
"/usr/local/bin",
|
|
@@ -59489,7 +59655,7 @@ function getPathHintEntries() {
|
|
|
59489
59655
|
}
|
|
59490
59656
|
function fileExists$1(candidate) {
|
|
59491
59657
|
if (!candidate) return null;
|
|
59492
|
-
const normalized =
|
|
59658
|
+
const normalized = nodePath.resolve(candidate);
|
|
59493
59659
|
try {
|
|
59494
59660
|
if (statSync(normalized).isFile()) return normalized;
|
|
59495
59661
|
} catch {}
|
|
@@ -59506,7 +59672,7 @@ function resolveCodexFromEnv() {
|
|
|
59506
59672
|
}
|
|
59507
59673
|
function resolveCodexFromPath() {
|
|
59508
59674
|
const pathValue = process.env.PATH ?? "";
|
|
59509
|
-
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)));
|
|
59510
59676
|
const names = process.platform === "win32" ? [
|
|
59511
59677
|
"codex.exe",
|
|
59512
59678
|
"codex.cmd",
|
|
@@ -59514,16 +59680,16 @@ function resolveCodexFromPath() {
|
|
|
59514
59680
|
"codex"
|
|
59515
59681
|
] : ["codex"];
|
|
59516
59682
|
for (const entry of entries) for (const name of names) {
|
|
59517
|
-
const candidate = fileExists$1(
|
|
59683
|
+
const candidate = fileExists$1(nodePath.join(entry, name));
|
|
59518
59684
|
if (candidate) return candidate;
|
|
59519
59685
|
}
|
|
59520
59686
|
return null;
|
|
59521
59687
|
}
|
|
59522
59688
|
function resolvePackageJsonPath(cliPath) {
|
|
59523
|
-
const normalized =
|
|
59524
|
-
const binDir =
|
|
59525
|
-
const packageDir =
|
|
59526
|
-
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"));
|
|
59527
59693
|
}
|
|
59528
59694
|
function readCodexCliVersion(cliPath) {
|
|
59529
59695
|
const packageJsonPath = resolvePackageJsonPath(cliPath);
|
|
@@ -59614,17 +59780,17 @@ function resolveNodeRuntime(env) {
|
|
|
59614
59780
|
...env
|
|
59615
59781
|
};
|
|
59616
59782
|
const homeDir = runtimeEnv.HOME ?? runtimeEnv.USERPROFILE ?? "";
|
|
59617
|
-
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);
|
|
59618
59784
|
const extraPathEntries = [
|
|
59619
59785
|
"/usr/local/bin",
|
|
59620
59786
|
"/opt/homebrew/bin",
|
|
59621
|
-
|
|
59622
|
-
|
|
59623
|
-
|
|
59787
|
+
nodePath.join(homeDir, ".local", "bin"),
|
|
59788
|
+
nodePath.join(homeDir, ".fnm", "aliases", "default", "bin"),
|
|
59789
|
+
nodePath.join(homeDir, ".fnm", "current", "bin"),
|
|
59624
59790
|
...pathHints
|
|
59625
59791
|
].filter(Boolean);
|
|
59626
59792
|
const currentPath = runtimeEnv.PATH ?? "";
|
|
59627
|
-
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);
|
|
59628
59794
|
const candidates = Array.from(new Set([
|
|
59629
59795
|
runtimeEnv.CODEX_NODE_RUNTIME?.trim(),
|
|
59630
59796
|
runtimeEnv.CODEX_NODE_BIN?.trim(),
|
|
@@ -59661,11 +59827,11 @@ function buildCodexCommand$1(binaryPath, args, env) {
|
|
|
59661
59827
|
//#endregion
|
|
59662
59828
|
//#region ../../packages/runtime-codex/src/codex/home.ts
|
|
59663
59829
|
function resolveHomeDir$1() {
|
|
59664
|
-
return process.env.HOME || process.env.USERPROFILE ||
|
|
59830
|
+
return process.env.HOME || process.env.USERPROFILE || nodeOs.homedir();
|
|
59665
59831
|
}
|
|
59666
59832
|
function expandHomePrefix(input, homeDir) {
|
|
59667
59833
|
if (input === "~") return homeDir;
|
|
59668
|
-
if (input.startsWith("~/") || input.startsWith("~\\")) return
|
|
59834
|
+
if (input.startsWith("~/") || input.startsWith("~\\")) return nodePath.join(homeDir, input.slice(2));
|
|
59669
59835
|
return input;
|
|
59670
59836
|
}
|
|
59671
59837
|
function normalizePathCandidate(value) {
|
|
@@ -59676,13 +59842,10 @@ function normalizePathCandidate(value) {
|
|
|
59676
59842
|
function normalizeCodexHomePath(input) {
|
|
59677
59843
|
const configured = normalizePathCandidate(input);
|
|
59678
59844
|
if (!configured) return null;
|
|
59679
|
-
return
|
|
59845
|
+
return nodePath.resolve(expandHomePrefix(configured, resolveHomeDir$1()));
|
|
59680
59846
|
}
|
|
59681
59847
|
function resolveDefaultCodexHomeDir() {
|
|
59682
|
-
return
|
|
59683
|
-
}
|
|
59684
|
-
function resolveProfileCodexHomeDir(profileName) {
|
|
59685
|
-
return path.join(getUserDataDir(), "profile-homes", profileName);
|
|
59848
|
+
return nodePath.join(resolveHomeDir$1(), ".codex");
|
|
59686
59849
|
}
|
|
59687
59850
|
function resolveCodexHomeDir(options) {
|
|
59688
59851
|
return normalizeCodexHomePath(options?.codexHomePath) ?? resolveDefaultCodexHomeDir();
|
|
@@ -59694,14 +59857,7 @@ async function resolveCodexRuntimeContext(options) {
|
|
|
59694
59857
|
profileName: null,
|
|
59695
59858
|
source: "explicit"
|
|
59696
59859
|
};
|
|
59697
|
-
const
|
|
59698
|
-
const profileName = normalizePathCandidate(state.app.lastProfileName);
|
|
59699
|
-
if (profileName && state.profilesByName[profileName]) return {
|
|
59700
|
-
codexHomePath: resolveProfileCodexHomeDir(profileName),
|
|
59701
|
-
profileName,
|
|
59702
|
-
source: "profile"
|
|
59703
|
-
};
|
|
59704
|
-
const configured = normalizeCodexHomePath(state.runtimeSettings?.codexHome);
|
|
59860
|
+
const configured = normalizeCodexHomePath((await getAppState()).runtimeSettings?.codexHome);
|
|
59705
59861
|
if (configured) return {
|
|
59706
59862
|
codexHomePath: configured,
|
|
59707
59863
|
profileName: null,
|
|
@@ -59778,19 +59934,19 @@ function buildCliEnv(codexPath, overrides = {}) {
|
|
|
59778
59934
|
...overrides
|
|
59779
59935
|
};
|
|
59780
59936
|
const currentPath = env.PATH ?? "";
|
|
59781
|
-
const codexDir = path
|
|
59937
|
+
const codexDir = path.dirname(codexPath);
|
|
59782
59938
|
const homeDir = process.env.HOME ?? "";
|
|
59783
|
-
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);
|
|
59784
59940
|
const segments = [...[
|
|
59785
59941
|
codexDir,
|
|
59786
59942
|
"/usr/local/bin",
|
|
59787
59943
|
"/opt/homebrew/bin",
|
|
59788
|
-
path
|
|
59789
|
-
path
|
|
59790
|
-
path
|
|
59944
|
+
path.join(homeDir, ".local", "bin"),
|
|
59945
|
+
path.join(homeDir, ".fnm", "aliases", "default", "bin"),
|
|
59946
|
+
path.join(homeDir, ".fnm", "current", "bin"),
|
|
59791
59947
|
...extraPathHints
|
|
59792
|
-
].filter(Boolean), ...currentPath.split(path
|
|
59793
|
-
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);
|
|
59794
59950
|
return env;
|
|
59795
59951
|
}
|
|
59796
59952
|
//#endregion
|
|
@@ -60747,7 +60903,7 @@ async function getActiveProfileContext() {
|
|
|
60747
60903
|
const profileKey = toProfileStorageKey(profileName);
|
|
60748
60904
|
return {
|
|
60749
60905
|
profileKey,
|
|
60750
|
-
parityStoreDir:
|
|
60906
|
+
parityStoreDir: nodePath.join(getUserDataDir(), PROFILE_ROOT_DIR, profileKey, PROFILE_PARITY_DIR)
|
|
60751
60907
|
};
|
|
60752
60908
|
}
|
|
60753
60909
|
async function fileExists(filePath) {
|
|
@@ -60759,10 +60915,10 @@ async function fileExists(filePath) {
|
|
|
60759
60915
|
}
|
|
60760
60916
|
}
|
|
60761
60917
|
function legacyStorePath() {
|
|
60762
|
-
return
|
|
60918
|
+
return nodePath.join(getUserDataDir(), LEGACY_STORE_FILE);
|
|
60763
60919
|
}
|
|
60764
60920
|
function legacyMigrationMarkerPath() {
|
|
60765
|
-
return
|
|
60921
|
+
return nodePath.join(getUserDataDir(), LEGACY_MIGRATION_MARKER_FILE);
|
|
60766
60922
|
}
|
|
60767
60923
|
async function readLegacyMigrationMarker() {
|
|
60768
60924
|
try {
|
|
@@ -60781,13 +60937,13 @@ async function migrateLegacyStoreIfNeeded(scopedStorePath) {
|
|
|
60781
60937
|
if (!await fileExists(legacyPath)) return;
|
|
60782
60938
|
const marker = await readLegacyMigrationMarker();
|
|
60783
60939
|
if (marker?.migratedToProfileKey && marker.migratedToProfileKey !== context.profileKey) return;
|
|
60784
|
-
await fs$1.mkdir(
|
|
60940
|
+
await fs$1.mkdir(nodePath.dirname(scopedStorePath), { recursive: true });
|
|
60785
60941
|
await fs$1.copyFile(legacyPath, scopedStorePath);
|
|
60786
60942
|
}
|
|
60787
60943
|
async function storePath() {
|
|
60788
60944
|
const context = await getActiveProfileContext();
|
|
60789
60945
|
await fs$1.mkdir(context.parityStoreDir, { recursive: true });
|
|
60790
|
-
const scopedPath =
|
|
60946
|
+
const scopedPath = nodePath.join(context.parityStoreDir, STORE_FILE);
|
|
60791
60947
|
await migrateLegacyStoreIfNeeded(scopedPath);
|
|
60792
60948
|
return scopedPath;
|
|
60793
60949
|
}
|
|
@@ -60798,7 +60954,7 @@ function toWorkspaceName(inputPath) {
|
|
|
60798
60954
|
function sanitizeWorkspace(entry) {
|
|
60799
60955
|
if (!entry || typeof entry !== "object") return null;
|
|
60800
60956
|
const id = typeof entry.id === "string" && entry.id.trim() ? entry.id.trim() : null;
|
|
60801
|
-
const workspacePath = typeof entry.path === "string" && entry.path.trim() ?
|
|
60957
|
+
const workspacePath = typeof entry.path === "string" && entry.path.trim() ? nodePath.resolve(entry.path) : null;
|
|
60802
60958
|
if (!id || !workspacePath) return null;
|
|
60803
60959
|
const name = typeof entry.name === "string" && entry.name.trim() ? entry.name.trim() : toWorkspaceName(workspacePath);
|
|
60804
60960
|
const branch = entry.worktree && typeof entry.worktree === "object" && typeof entry.worktree.branch === "string" ? entry.worktree.branch.trim() || "main" : "main";
|
|
@@ -60917,7 +61073,7 @@ function ensureThreadEntry(store, threadId) {
|
|
|
60917
61073
|
return created;
|
|
60918
61074
|
}
|
|
60919
61075
|
function resolveOverridesDbPath(stateDir) {
|
|
60920
|
-
return
|
|
61076
|
+
return nodePath.join(stateDir, "state.sqlite");
|
|
60921
61077
|
}
|
|
60922
61078
|
async function readExternalThreadOverrides(stateDir) {
|
|
60923
61079
|
return await readDocument(resolveOverridesDbPath(stateDir), EXTERNAL_THREAD_OVERRIDES_DOCUMENT, (value) => {
|
|
@@ -61058,6 +61214,62 @@ let externalCodexThreadSyncWatcherSettleTimer = null;
|
|
|
61058
61214
|
let externalCodexThreadSyncWatcherPendingTrigger = null;
|
|
61059
61215
|
const externalSyncAppServerLeaseCount = /* @__PURE__ */ new Map();
|
|
61060
61216
|
let externalCodexThreadSyncRefreshQueue = null;
|
|
61217
|
+
let externalCodexThreadSyncProgressReporter = null;
|
|
61218
|
+
function makeEmptyExternalThreadSyncProjectStats(syncTarget) {
|
|
61219
|
+
return {
|
|
61220
|
+
projectId: syncTarget.projectId,
|
|
61221
|
+
workspaceRoot: syncTarget.workspace.path,
|
|
61222
|
+
title: syncTarget.workspace.name,
|
|
61223
|
+
scannedThreads: 0,
|
|
61224
|
+
createdThreads: 0,
|
|
61225
|
+
restoredThreads: 0,
|
|
61226
|
+
hydratedThreads: 0,
|
|
61227
|
+
importedMessages: 0,
|
|
61228
|
+
importedActivities: 0,
|
|
61229
|
+
importedPlans: 0
|
|
61230
|
+
};
|
|
61231
|
+
}
|
|
61232
|
+
function sumExternalThreadSyncProjectStats(projects) {
|
|
61233
|
+
return projects.reduce((total, project) => ({
|
|
61234
|
+
scannedThreads: total.scannedThreads + project.scannedThreads,
|
|
61235
|
+
createdThreads: total.createdThreads + project.createdThreads,
|
|
61236
|
+
restoredThreads: total.restoredThreads + project.restoredThreads,
|
|
61237
|
+
hydratedThreads: total.hydratedThreads + project.hydratedThreads,
|
|
61238
|
+
importedMessages: total.importedMessages + project.importedMessages,
|
|
61239
|
+
importedActivities: total.importedActivities + project.importedActivities,
|
|
61240
|
+
importedPlans: total.importedPlans + project.importedPlans
|
|
61241
|
+
}), {
|
|
61242
|
+
scannedThreads: 0,
|
|
61243
|
+
createdThreads: 0,
|
|
61244
|
+
restoredThreads: 0,
|
|
61245
|
+
hydratedThreads: 0,
|
|
61246
|
+
importedMessages: 0,
|
|
61247
|
+
importedActivities: 0,
|
|
61248
|
+
importedPlans: 0
|
|
61249
|
+
});
|
|
61250
|
+
}
|
|
61251
|
+
function makeExternalThreadSyncStats(input) {
|
|
61252
|
+
return {
|
|
61253
|
+
bootstrappedProjectCount: input?.bootstrappedProjectCount ?? 0,
|
|
61254
|
+
syncedProjectCount: input?.syncedProjectCount ?? 0,
|
|
61255
|
+
createdThreadCount: input?.createdThreadCount ?? 0,
|
|
61256
|
+
restoredThreadCount: input?.restoredThreadCount ?? 0,
|
|
61257
|
+
foregroundSummaryCount: input?.foregroundSummaryCount ?? 0,
|
|
61258
|
+
foregroundHydratedThreadCount: input?.foregroundHydratedThreadCount ?? 0,
|
|
61259
|
+
foregroundImportedMessageCount: input?.foregroundImportedMessageCount ?? 0,
|
|
61260
|
+
foregroundImportedActivityCount: input?.foregroundImportedActivityCount ?? 0,
|
|
61261
|
+
foregroundImportedPlanCount: input?.foregroundImportedPlanCount ?? 0,
|
|
61262
|
+
backgroundBackfillProjectCount: input?.backgroundBackfillProjectCount ?? 0,
|
|
61263
|
+
parityWorkspaceCount: input?.parityWorkspaceCount ?? 0,
|
|
61264
|
+
hadErrors: input?.hadErrors ?? false
|
|
61265
|
+
};
|
|
61266
|
+
}
|
|
61267
|
+
function publishExternalThreadSyncProgress(payload) {
|
|
61268
|
+
if (!externalCodexThreadSyncProgressReporter) return;
|
|
61269
|
+
Promise.resolve(externalCodexThreadSyncProgressReporter(payload)).catch((error) => {
|
|
61270
|
+
console.warn("[t3 external thread sync] progress publish failed", { error });
|
|
61271
|
+
});
|
|
61272
|
+
}
|
|
61061
61273
|
function asRecord$3(value) {
|
|
61062
61274
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
61063
61275
|
}
|
|
@@ -61146,6 +61358,9 @@ function closeExternalCodexThreadSyncWatcher() {
|
|
|
61146
61358
|
function setExternalCodexThreadSyncRefreshQueue(queue) {
|
|
61147
61359
|
externalCodexThreadSyncRefreshQueue = queue ? { offer: queue } : null;
|
|
61148
61360
|
}
|
|
61361
|
+
function setExternalCodexThreadSyncProgressReporter(reporter) {
|
|
61362
|
+
externalCodexThreadSyncProgressReporter = reporter;
|
|
61363
|
+
}
|
|
61149
61364
|
async function requestExternalCodexThreadSyncRefresh(trigger) {
|
|
61150
61365
|
if (!externalCodexThreadSyncRefreshQueue) return false;
|
|
61151
61366
|
try {
|
|
@@ -61180,7 +61395,7 @@ function collectExternalCodexThreadSyncDirectoryWatchPaths(rootPath) {
|
|
|
61180
61395
|
while (stack.length > 0) {
|
|
61181
61396
|
const currentPath = stack.pop();
|
|
61182
61397
|
if (!currentPath) continue;
|
|
61183
|
-
const resolvedCurrentPath =
|
|
61398
|
+
const resolvedCurrentPath = nodePath.resolve(currentPath);
|
|
61184
61399
|
if (seenPaths.has(resolvedCurrentPath)) continue;
|
|
61185
61400
|
seenPaths.add(resolvedCurrentPath);
|
|
61186
61401
|
let stat;
|
|
@@ -61199,7 +61414,7 @@ function collectExternalCodexThreadSyncDirectoryWatchPaths(rootPath) {
|
|
|
61199
61414
|
}
|
|
61200
61415
|
for (const entry of entries) {
|
|
61201
61416
|
if (!entry.isDirectory()) continue;
|
|
61202
|
-
stack.push(
|
|
61417
|
+
stack.push(nodePath.join(resolvedCurrentPath, entry.name));
|
|
61203
61418
|
}
|
|
61204
61419
|
}
|
|
61205
61420
|
return collectedPaths;
|
|
@@ -61208,7 +61423,7 @@ function collectExternalCodexThreadSyncWatchTargets(homePath) {
|
|
|
61208
61423
|
const recursive = supportsRecursiveExternalCodexThreadSyncWatcher();
|
|
61209
61424
|
const targets = [];
|
|
61210
61425
|
for (const relativePath of EXTERNAL_SYNC_WATCH_DIRECTORY_NAMES) {
|
|
61211
|
-
const absolutePath =
|
|
61426
|
+
const absolutePath = nodePath.join(homePath, relativePath);
|
|
61212
61427
|
if (!fs.existsSync(absolutePath)) continue;
|
|
61213
61428
|
if (recursive) {
|
|
61214
61429
|
targets.push({
|
|
@@ -61220,7 +61435,7 @@ function collectExternalCodexThreadSyncWatchTargets(homePath) {
|
|
|
61220
61435
|
}
|
|
61221
61436
|
for (const watchPath of collectExternalCodexThreadSyncDirectoryWatchPaths(absolutePath)) targets.push({
|
|
61222
61437
|
watchPath,
|
|
61223
|
-
label:
|
|
61438
|
+
label: nodePath.relative(homePath, watchPath) || ".",
|
|
61224
61439
|
recursive: false
|
|
61225
61440
|
});
|
|
61226
61441
|
}
|
|
@@ -61232,7 +61447,7 @@ function collectExternalCodexThreadSyncWatchTargets(homePath) {
|
|
|
61232
61447
|
return targets;
|
|
61233
61448
|
}
|
|
61234
61449
|
function startExternalCodexThreadSyncWatcher(homePath) {
|
|
61235
|
-
const resolvedHomePath =
|
|
61450
|
+
const resolvedHomePath = nodePath.resolve(homePath);
|
|
61236
61451
|
if (externalCodexThreadSyncWatchers.length > 0 && externalCodexThreadSyncWatcherHomePath === resolvedHomePath) return;
|
|
61237
61452
|
closeExternalCodexThreadSyncWatcher();
|
|
61238
61453
|
try {
|
|
@@ -61263,7 +61478,7 @@ function startExternalCodexThreadSyncWatcher(homePath) {
|
|
|
61263
61478
|
}
|
|
61264
61479
|
}
|
|
61265
61480
|
function resolveStateDbPath(stateDir) {
|
|
61266
|
-
return
|
|
61481
|
+
return nodePath.join(stateDir, "state.sqlite");
|
|
61267
61482
|
}
|
|
61268
61483
|
function asString$6(value) {
|
|
61269
61484
|
return typeof value === "string" ? value : value == null ? "" : String(value);
|
|
@@ -61550,7 +61765,7 @@ function resolveLegacyFilePath(source) {
|
|
|
61550
61765
|
if (normalized.startsWith("~/")) {
|
|
61551
61766
|
const home = process.env.HOME?.trim();
|
|
61552
61767
|
if (!home) return null;
|
|
61553
|
-
return
|
|
61768
|
+
return nodePath.join(home, normalized.slice(2));
|
|
61554
61769
|
}
|
|
61555
61770
|
return normalized;
|
|
61556
61771
|
}
|
|
@@ -61586,7 +61801,7 @@ async function materializeLegacyImageAttachment(input) {
|
|
|
61586
61801
|
mimeType = Mime_default.getType(filePath) ?? "";
|
|
61587
61802
|
if (!mimeType.startsWith("image/")) return null;
|
|
61588
61803
|
bytes = await fs$1.readFile(filePath);
|
|
61589
|
-
name =
|
|
61804
|
+
name = nodePath.basename(filePath) || `image${inferImageExtension({ mimeType })}`;
|
|
61590
61805
|
}
|
|
61591
61806
|
if (bytes.byteLength === 0 || bytes.byteLength > 10485760) return null;
|
|
61592
61807
|
const attachmentId = createDeterministicLegacyAttachmentId(input.threadId, input.attachmentKey);
|
|
@@ -61603,7 +61818,7 @@ async function materializeLegacyImageAttachment(input) {
|
|
|
61603
61818
|
attachment
|
|
61604
61819
|
});
|
|
61605
61820
|
if (!attachmentPath) return null;
|
|
61606
|
-
await fs$1.mkdir(
|
|
61821
|
+
await fs$1.mkdir(nodePath.dirname(attachmentPath), { recursive: true });
|
|
61607
61822
|
await fs$1.writeFile(attachmentPath, bytes);
|
|
61608
61823
|
return attachment;
|
|
61609
61824
|
} catch {
|
|
@@ -61922,7 +62137,7 @@ async function readImportMarker(stateDir) {
|
|
|
61922
62137
|
return { version };
|
|
61923
62138
|
});
|
|
61924
62139
|
if (stored) return stored;
|
|
61925
|
-
const markerPath =
|
|
62140
|
+
const markerPath = nodePath.join(stateDir, EXTERNAL_SYNC_MARKER_FILE);
|
|
61926
62141
|
try {
|
|
61927
62142
|
const raw = await fs$1.readFile(markerPath, "utf8");
|
|
61928
62143
|
const version = asFiniteNumber(asRecord$3(JSON.parse(raw))?.version);
|
|
@@ -61973,7 +62188,7 @@ async function readLegacyThreadByCandidateIds(appServer, candidateIds) {
|
|
|
61973
62188
|
}
|
|
61974
62189
|
function shouldReadLegacyThreadForImport(input) {
|
|
61975
62190
|
if (!input.existingThread) return true;
|
|
61976
|
-
if (input.existingThread.deletedAt !== null) return
|
|
62191
|
+
if (input.existingThread.deletedAt !== null) return input.restoreDeletedImportedThread === true;
|
|
61977
62192
|
if (input.forceRereadExistingThreads) return true;
|
|
61978
62193
|
const legacyUpdatedAtMs = toEpochMs(input.legacyThreadSummary.updatedAt ?? input.legacyThreadSummary.createdAt, 0);
|
|
61979
62194
|
if (legacyUpdatedAtMs <= 0) return false;
|
|
@@ -61981,7 +62196,7 @@ function shouldReadLegacyThreadForImport(input) {
|
|
|
61981
62196
|
}
|
|
61982
62197
|
async function writeImportMarker(stateDir, payload) {
|
|
61983
62198
|
await writeDocument(resolveStateDbPath(stateDir), EXTERNAL_SYNC_MARKER_DOCUMENT, payload);
|
|
61984
|
-
await fs$1.rm(
|
|
62199
|
+
await fs$1.rm(nodePath.join(stateDir, EXTERNAL_SYNC_MARKER_FILE), { force: true }).catch(() => void 0);
|
|
61985
62200
|
}
|
|
61986
62201
|
function resolveLegacyThreadTimestamps(thread) {
|
|
61987
62202
|
const createdAtMs = toEpochMs(thread.createdAt ?? thread.updatedAt, Date.now());
|
|
@@ -62004,6 +62219,9 @@ function shouldRefreshImportedMessage(input) {
|
|
|
62004
62219
|
function hasImportedThreadArtifacts(state) {
|
|
62005
62220
|
return state.messageIds.size > 0 || state.activityIds.size > 0 || state.planIds.size > 0;
|
|
62006
62221
|
}
|
|
62222
|
+
function shouldRestoreDeletedImportedThread(input) {
|
|
62223
|
+
return input.existingThread !== void 0 && input.existingThread.deletedAt !== null && input.existingThread.origin === "external-import" && input.existingThread.projectId !== input.projectId;
|
|
62224
|
+
}
|
|
62007
62225
|
function readProviderThreadIdFromResumeCursor(resumeCursor) {
|
|
62008
62226
|
const cursor = asRecord$3(resumeCursor);
|
|
62009
62227
|
return firstNonEmptyString(cursor?.threadId, cursor?.thread_id, cursor?.conversationId, cursor?.conversation_id) ?? null;
|
|
@@ -62053,6 +62271,7 @@ function syncExternalThreadSummary(input) {
|
|
|
62053
62271
|
const threadIdRaw = asTrimmedString$1(input.summary.id);
|
|
62054
62272
|
if (!threadIdRaw) return {
|
|
62055
62273
|
createdThread: false,
|
|
62274
|
+
restoredThread: false,
|
|
62056
62275
|
overridesChanged: false
|
|
62057
62276
|
};
|
|
62058
62277
|
const threadId = ThreadId.makeUnsafe(threadIdRaw);
|
|
@@ -62070,9 +62289,14 @@ function syncExternalThreadSummary(input) {
|
|
|
62070
62289
|
orchestrationEngine: input.orchestrationEngine
|
|
62071
62290
|
})) return {
|
|
62072
62291
|
createdThread: false,
|
|
62292
|
+
restoredThread: false,
|
|
62073
62293
|
overridesChanged
|
|
62074
62294
|
};
|
|
62075
|
-
const
|
|
62295
|
+
const restoreDeletedImportedThread = shouldRestoreDeletedImportedThread({
|
|
62296
|
+
existingThread,
|
|
62297
|
+
projectId: input.projectId
|
|
62298
|
+
});
|
|
62299
|
+
const effectiveUpdatedAt = existingThread && existingThread.deletedAt === null ? latestIsoTimestamp(existingThread.updatedAt, updatedAt) : updatedAt;
|
|
62076
62300
|
const nextBranch = input.workspace.kind === "worktree" ? firstNonEmptyString(input.workspace.worktree?.branch) : null;
|
|
62077
62301
|
const nextWorktreePath = input.workspace.kind === "worktree" ? input.workspace.path : null;
|
|
62078
62302
|
if (suppressed) {
|
|
@@ -62091,18 +62315,22 @@ function syncExternalThreadSummary(input) {
|
|
|
62091
62315
|
}
|
|
62092
62316
|
return {
|
|
62093
62317
|
createdThread: false,
|
|
62318
|
+
restoredThread: false,
|
|
62094
62319
|
overridesChanged
|
|
62095
62320
|
};
|
|
62096
62321
|
}
|
|
62097
|
-
if (existingThread && existingThread.deletedAt !== null) return {
|
|
62322
|
+
if (existingThread && existingThread.deletedAt !== null && !restoreDeletedImportedThread) return {
|
|
62098
62323
|
createdThread: false,
|
|
62324
|
+
restoredThread: false,
|
|
62099
62325
|
overridesChanged
|
|
62100
62326
|
};
|
|
62101
|
-
|
|
62327
|
+
const shouldCreateThread = !input.state.threadIds.has(threadId);
|
|
62328
|
+
if (existingThread && existingThread.deletedAt === null && existingThread.projectId === input.projectId && existingThread.title === title && existingThread.updatedAt === effectiveUpdatedAt && existingThread.origin === "external-import" && existingThread.branch === nextBranch && existingThread.worktreePath === nextWorktreePath) return {
|
|
62102
62329
|
createdThread: false,
|
|
62330
|
+
restoredThread: false,
|
|
62103
62331
|
overridesChanged
|
|
62104
62332
|
};
|
|
62105
|
-
if (
|
|
62333
|
+
if (shouldCreateThread) {
|
|
62106
62334
|
yield* input.orchestrationEngine.dispatch({
|
|
62107
62335
|
type: "thread.create",
|
|
62108
62336
|
commandId: CommandId.makeUnsafe(makeServerCommandId()),
|
|
@@ -62124,10 +62352,12 @@ function syncExternalThreadSummary(input) {
|
|
|
62124
62352
|
type: "thread.meta.update",
|
|
62125
62353
|
commandId: CommandId.makeUnsafe(makeServerCommandId()),
|
|
62126
62354
|
threadId,
|
|
62355
|
+
...existingThread?.projectId !== input.projectId ? { projectId: input.projectId } : {},
|
|
62127
62356
|
title,
|
|
62128
62357
|
origin: "external-import",
|
|
62129
62358
|
branch: nextBranch,
|
|
62130
62359
|
worktreePath: nextWorktreePath,
|
|
62360
|
+
...restoreDeletedImportedThread ? { deletedAt: null } : {},
|
|
62131
62361
|
updatedAt: effectiveUpdatedAt
|
|
62132
62362
|
});
|
|
62133
62363
|
input.state.threadById.set(threadId, {
|
|
@@ -62142,16 +62372,28 @@ function syncExternalThreadSummary(input) {
|
|
|
62142
62372
|
});
|
|
62143
62373
|
return {
|
|
62144
62374
|
createdThread: !existingThread,
|
|
62375
|
+
restoredThread: restoreDeletedImportedThread,
|
|
62145
62376
|
overridesChanged
|
|
62146
62377
|
};
|
|
62147
62378
|
});
|
|
62148
62379
|
}
|
|
62149
62380
|
function importExternalThreadArtifacts(input) {
|
|
62150
62381
|
return gen(function* () {
|
|
62382
|
+
const existingThread = input.state.threadById.get(input.threadId);
|
|
62383
|
+
const restoreDeletedImportedThread = shouldRestoreDeletedImportedThread({
|
|
62384
|
+
existingThread,
|
|
62385
|
+
projectId: input.projectId
|
|
62386
|
+
});
|
|
62387
|
+
if (existingThread?.deletedAt !== null && !restoreDeletedImportedThread) return {
|
|
62388
|
+
createdThread: false,
|
|
62389
|
+
importedMessageCount: 0,
|
|
62390
|
+
importedActivityCount: 0,
|
|
62391
|
+
importedPlanCount: 0,
|
|
62392
|
+
overridesChanged: false
|
|
62393
|
+
};
|
|
62151
62394
|
const isNewThread = !input.state.threadIds.has(input.threadId);
|
|
62152
62395
|
const cachedThreadState = input.state.threadStateById.get(input.threadId);
|
|
62153
|
-
const existingThreadState =
|
|
62154
|
-
const existingThread = input.state.threadById.get(input.threadId);
|
|
62396
|
+
const existingThreadState = input.state.threadIds.has(input.threadId) && (!cachedThreadState || cachedThreadState.messageIds.size === 0 && cachedThreadState.activityIds.size === 0 && cachedThreadState.planIds.size === 0) ? yield* loadPersistedThreadImportState(input.importStateRepositories, input.threadId).pipe(orElseSucceed(() => createEmptyThreadImportState())) : cachedThreadState ?? createEmptyThreadImportState();
|
|
62155
62397
|
const existingThreadReadModel = input.state.threadReadModelById.get(input.threadId);
|
|
62156
62398
|
const localContinuationDedupState = existingThreadReadModel ? buildLocalContinuationDedupState({
|
|
62157
62399
|
thread: existingThreadReadModel,
|
|
@@ -62174,7 +62416,7 @@ function importExternalThreadArtifacts(input) {
|
|
|
62174
62416
|
importedPlanCount: 0,
|
|
62175
62417
|
overridesChanged
|
|
62176
62418
|
};
|
|
62177
|
-
if (suppressed
|
|
62419
|
+
if (suppressed) return {
|
|
62178
62420
|
createdThread: false,
|
|
62179
62421
|
importedMessageCount: 0,
|
|
62180
62422
|
importedActivityCount: 0,
|
|
@@ -62254,16 +62496,18 @@ function importExternalThreadArtifacts(input) {
|
|
|
62254
62496
|
importedActivityCount += 1;
|
|
62255
62497
|
}
|
|
62256
62498
|
const importedArtifactCount = importedMessageCount + importedPlanCount + importedActivityCount;
|
|
62257
|
-
const effectiveUpdatedAt = existingThread === void 0 ? input.importArtifacts.latestUpdatedAt : importedArtifactCount === 0 ? existingThread.updatedAt : hadImportedArtifactsBeforeSync ? latestIsoTimestamp(existingThread.updatedAt, input.importArtifacts.latestUpdatedAt) : latestIsoTimestamp(existingThread.updatedAt, input.importArtifacts.latestArtifactUpdatedAt);
|
|
62258
|
-
if (!isNewThread && (existingThread === void 0 || existingThread.title !== effectiveTitle || existingThread.origin !== "external-import" || existingThread.branch !== input.importArtifacts.branch || existingThread.worktreePath !== input.importArtifacts.worktreePath || existingThread.updatedAt !== effectiveUpdatedAt)) yield* input.orchestrationEngine.dispatch({
|
|
62499
|
+
const effectiveUpdatedAt = existingThread === void 0 ? input.importArtifacts.latestUpdatedAt : existingThread.deletedAt !== null ? input.importArtifacts.latestUpdatedAt : importedArtifactCount === 0 ? existingThread.updatedAt : hadImportedArtifactsBeforeSync ? latestIsoTimestamp(existingThread.updatedAt, input.importArtifacts.latestUpdatedAt) : latestIsoTimestamp(existingThread.updatedAt, input.importArtifacts.latestArtifactUpdatedAt);
|
|
62500
|
+
if (!isNewThread && (existingThread === void 0 || existingThread.projectId !== input.projectId || restoreDeletedImportedThread || existingThread.title !== effectiveTitle || existingThread.origin !== "external-import" || existingThread.branch !== input.importArtifacts.branch || existingThread.worktreePath !== input.importArtifacts.worktreePath || existingThread.updatedAt !== effectiveUpdatedAt)) yield* input.orchestrationEngine.dispatch({
|
|
62259
62501
|
type: "thread.meta.update",
|
|
62260
62502
|
commandId: CommandId.makeUnsafe(makeServerCommandId()),
|
|
62261
62503
|
threadId: input.threadId,
|
|
62504
|
+
...existingThread?.projectId !== input.projectId ? { projectId: input.projectId } : {},
|
|
62262
62505
|
title: effectiveTitle,
|
|
62263
62506
|
modelSelection: input.importArtifacts.modelSelection,
|
|
62264
62507
|
origin: "external-import",
|
|
62265
62508
|
branch: input.importArtifacts.branch,
|
|
62266
62509
|
worktreePath: input.importArtifacts.worktreePath,
|
|
62510
|
+
...restoreDeletedImportedThread ? { deletedAt: null } : {},
|
|
62267
62511
|
updatedAt: effectiveUpdatedAt
|
|
62268
62512
|
});
|
|
62269
62513
|
input.state.threadById.set(input.threadId, {
|
|
@@ -62413,9 +62657,9 @@ const syncPassiveImportedThreadIfNeeded = (input) => gen(function* () {
|
|
|
62413
62657
|
})).overridesChanged) yield* tryPromise(() => writeExternalThreadOverrides(input.stateDir, overridesStore)).pipe(ignore);
|
|
62414
62658
|
return "continue";
|
|
62415
62659
|
});
|
|
62416
|
-
const
|
|
62660
|
+
const runExternalCodexThreadSyncWithOptions = (options) => gen(function* () {
|
|
62417
62661
|
const config = yield* ServerConfig$1;
|
|
62418
|
-
if (config.mode !== "desktop") return;
|
|
62662
|
+
if (config.mode !== "desktop") return makeExternalThreadSyncStats();
|
|
62419
62663
|
const orchestrationEngine = yield* OrchestrationEngineService;
|
|
62420
62664
|
const providerRuntimeRepository = yield* ProviderSessionRuntimeRepository;
|
|
62421
62665
|
const importStateRepositories = {
|
|
@@ -62457,7 +62701,7 @@ const runExternalCodexThreadSync = gen(function* () {
|
|
|
62457
62701
|
}
|
|
62458
62702
|
const syncTargets = [];
|
|
62459
62703
|
for (const project of readModel.projects) {
|
|
62460
|
-
if (project.deletedAt !== null || isGeneralChatWorkspaceRoot(project.workspaceRoot) || isAgentChatWorkspaceRoot(project.workspaceRoot)) continue;
|
|
62704
|
+
if (options?.projectId !== void 0 && project.id !== options.projectId || project.deletedAt !== null || isGeneralChatWorkspaceRoot(project.workspaceRoot) || isAgentChatWorkspaceRoot(project.workspaceRoot)) continue;
|
|
62461
62705
|
const workspaceRoot = project.workspaceRoot;
|
|
62462
62706
|
const rootKey = projectRootKey(workspaceRoot);
|
|
62463
62707
|
if (projectIdByRootKey.has(rootKey)) continue;
|
|
@@ -62475,8 +62719,8 @@ const runExternalCodexThreadSync = gen(function* () {
|
|
|
62475
62719
|
});
|
|
62476
62720
|
}
|
|
62477
62721
|
let bootstrappedProjectCount = 0;
|
|
62478
|
-
let remainingBootstrapProjects = (yield* promise(() => readProjectPolicy(readModel))).remainingProjects;
|
|
62479
|
-
for (const workspace of importableParityWorkspaces) {
|
|
62722
|
+
let remainingBootstrapProjects = options?.projectId === void 0 ? (yield* promise(() => readProjectPolicy(readModel))).remainingProjects : 0;
|
|
62723
|
+
for (const workspace of options?.projectId === void 0 ? importableParityWorkspaces : []) {
|
|
62480
62724
|
const workspaceRoot = workspace.path;
|
|
62481
62725
|
const workspaceRootCanonicalKey = projectRootKey(workspaceRoot);
|
|
62482
62726
|
if (projectIdByRootKey.has(workspaceRootCanonicalKey)) continue;
|
|
@@ -62519,28 +62763,67 @@ const runExternalCodexThreadSync = gen(function* () {
|
|
|
62519
62763
|
bootstrappedProjectCount += 1;
|
|
62520
62764
|
if (remainingBootstrapProjects !== null && remainingBootstrapProjects > 0) remainingBootstrapProjects -= 1;
|
|
62521
62765
|
}
|
|
62522
|
-
|
|
62766
|
+
let hadErrors = false;
|
|
62767
|
+
const syncRunId = crypto.randomUUID();
|
|
62768
|
+
const syncStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
62769
|
+
const projectStatsByProjectId = new Map(syncTargets.map((target) => [target.projectId, makeEmptyExternalThreadSyncProjectStats(target)]));
|
|
62770
|
+
const publishSyncProgress = (status, input) => {
|
|
62771
|
+
const projects = Array.from(projectStatsByProjectId.values());
|
|
62772
|
+
const totals = sumExternalThreadSyncProjectStats(projects);
|
|
62773
|
+
publishExternalThreadSyncProgress({
|
|
62774
|
+
status,
|
|
62775
|
+
runId: syncRunId,
|
|
62776
|
+
startedAt: syncStartedAt,
|
|
62777
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
62778
|
+
...input?.finishedAt ? { finishedAt: input.finishedAt } : {},
|
|
62779
|
+
currentProjectIndex: input?.currentProjectIndex ?? 0,
|
|
62780
|
+
totalProjects: syncTargets.length,
|
|
62781
|
+
...input?.project ? { project: input.project } : {},
|
|
62782
|
+
...status === "completed" || status === "failed" ? { projects } : {},
|
|
62783
|
+
...totals,
|
|
62784
|
+
hadErrors
|
|
62785
|
+
});
|
|
62786
|
+
};
|
|
62787
|
+
if (syncTargets.length === 0) {
|
|
62788
|
+
publishSyncProgress("completed", { finishedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
62789
|
+
return makeExternalThreadSyncStats({
|
|
62790
|
+
bootstrappedProjectCount,
|
|
62791
|
+
parityWorkspaceCount: importableParityWorkspaces.length
|
|
62792
|
+
});
|
|
62793
|
+
}
|
|
62794
|
+
publishSyncProgress("started");
|
|
62523
62795
|
let createdThreadCount = 0;
|
|
62796
|
+
let restoredThreadCount = 0;
|
|
62524
62797
|
let foregroundSummaryCount = 0;
|
|
62525
62798
|
let foregroundHydratedThreadCount = 0;
|
|
62526
62799
|
let foregroundImportedMessageCount = 0;
|
|
62527
62800
|
let foregroundImportedActivityCount = 0;
|
|
62528
62801
|
let foregroundImportedPlanCount = 0;
|
|
62529
|
-
let hadErrors = false;
|
|
62530
62802
|
const backgroundBackfillPlans = [];
|
|
62531
62803
|
const lease = yield* tryPromise(() => acquireExternalSyncAppServer(runtimeContext.codexHomePath));
|
|
62532
62804
|
try {
|
|
62533
62805
|
const appServer = lease.appServer;
|
|
62534
|
-
for (const syncTarget of syncTargets) {
|
|
62806
|
+
for (const [syncTargetIndex, syncTarget] of syncTargets.entries()) {
|
|
62535
62807
|
const { projectId, workspace } = syncTarget;
|
|
62808
|
+
const currentProjectIndex = syncTargetIndex + 1;
|
|
62809
|
+
const projectStats = projectStatsByProjectId.get(projectId) ?? makeEmptyExternalThreadSyncProjectStats(syncTarget);
|
|
62810
|
+
projectStatsByProjectId.set(projectId, projectStats);
|
|
62811
|
+
publishSyncProgress("project", {
|
|
62812
|
+
project: projectStats,
|
|
62813
|
+
currentProjectIndex
|
|
62814
|
+
});
|
|
62536
62815
|
let firstPage = null;
|
|
62537
62816
|
try {
|
|
62538
62817
|
firstPage = yield* tryPromise(() => listLegacyThreadPage(appServer, workspace, {
|
|
62539
|
-
limit: EXTERNAL_SYNC_FOREGROUND_THREAD_LIMIT,
|
|
62818
|
+
limit: options?.projectId !== void 0 ? EXTERNAL_THREAD_PAGE_SIZE : EXTERNAL_SYNC_FOREGROUND_THREAD_LIMIT,
|
|
62540
62819
|
sortKey: "updated_at"
|
|
62541
62820
|
}));
|
|
62542
62821
|
} catch (cause) {
|
|
62543
62822
|
hadErrors = true;
|
|
62823
|
+
publishSyncProgress("project", {
|
|
62824
|
+
project: projectStats,
|
|
62825
|
+
currentProjectIndex
|
|
62826
|
+
});
|
|
62544
62827
|
yield* logWarning$1("failed to list external Codex threads for project", {
|
|
62545
62828
|
cause,
|
|
62546
62829
|
projectId,
|
|
@@ -62550,15 +62833,25 @@ const runExternalCodexThreadSync = gen(function* () {
|
|
|
62550
62833
|
continue;
|
|
62551
62834
|
}
|
|
62552
62835
|
if (!firstPage) continue;
|
|
62836
|
+
projectStats.scannedThreads += firstPage.threads.length;
|
|
62837
|
+
publishSyncProgress("project", {
|
|
62838
|
+
project: projectStats,
|
|
62839
|
+
currentProjectIndex
|
|
62840
|
+
});
|
|
62553
62841
|
for (const summary of firstPage.threads) {
|
|
62554
62842
|
const threadIdRaw = asTrimmedString$1(summary.id);
|
|
62555
62843
|
const threadId = threadIdRaw ? ThreadId.makeUnsafe(threadIdRaw) : null;
|
|
62556
62844
|
const existingThread = threadId ? state.threadById.get(threadId) : void 0;
|
|
62557
62845
|
const existingReadModelThread = threadId ? readModelThreadById.get(threadId) : void 0;
|
|
62558
|
-
const
|
|
62846
|
+
const restoreDeletedImportedThread = shouldRestoreDeletedImportedThread({
|
|
62847
|
+
existingThread,
|
|
62848
|
+
projectId
|
|
62849
|
+
});
|
|
62850
|
+
const shouldHydrateForegroundThread = threadIdRaw.length > 0 && (existingThread === void 0 || existingThread.deletedAt === null || restoreDeletedImportedThread) && (shouldReadLegacyThreadForImport({
|
|
62559
62851
|
existingThread,
|
|
62560
62852
|
legacyThreadSummary: summary,
|
|
62561
|
-
forceRereadExistingThreads: false
|
|
62853
|
+
forceRereadExistingThreads: false,
|
|
62854
|
+
restoreDeletedImportedThread
|
|
62562
62855
|
}) || (existingReadModelThread ? needsExternalThreadHydration(existingReadModelThread) : false));
|
|
62563
62856
|
const summaryResult = yield* syncExternalThreadSummary({
|
|
62564
62857
|
summary,
|
|
@@ -62569,9 +62862,17 @@ const runExternalCodexThreadSync = gen(function* () {
|
|
|
62569
62862
|
orchestrationEngine
|
|
62570
62863
|
});
|
|
62571
62864
|
const createdThread = summaryResult.createdThread;
|
|
62865
|
+
const restoredThread = summaryResult.restoredThread;
|
|
62572
62866
|
overridesChanged ||= summaryResult.overridesChanged;
|
|
62573
62867
|
foregroundSummaryCount += 1;
|
|
62574
|
-
if (createdThread)
|
|
62868
|
+
if (createdThread) {
|
|
62869
|
+
createdThreadCount += 1;
|
|
62870
|
+
projectStats.createdThreads += 1;
|
|
62871
|
+
}
|
|
62872
|
+
if (restoredThread) {
|
|
62873
|
+
restoredThreadCount += 1;
|
|
62874
|
+
projectStats.restoredThreads += 1;
|
|
62875
|
+
}
|
|
62575
62876
|
if (!shouldHydrateForegroundThread || threadIdRaw.length === 0) continue;
|
|
62576
62877
|
try {
|
|
62577
62878
|
const legacyThread = yield* tryPromise(() => readLegacyThread(appServer, threadIdRaw));
|
|
@@ -62596,7 +62897,15 @@ const runExternalCodexThreadSync = gen(function* () {
|
|
|
62596
62897
|
foregroundImportedMessageCount += importResult.importedMessageCount;
|
|
62597
62898
|
foregroundImportedActivityCount += importResult.importedActivityCount;
|
|
62598
62899
|
foregroundImportedPlanCount += importResult.importedPlanCount;
|
|
62900
|
+
projectStats.hydratedThreads += 1;
|
|
62901
|
+
projectStats.importedMessages += importResult.importedMessageCount;
|
|
62902
|
+
projectStats.importedActivities += importResult.importedActivityCount;
|
|
62903
|
+
projectStats.importedPlans += importResult.importedPlanCount;
|
|
62599
62904
|
overridesChanged ||= importResult.overridesChanged;
|
|
62905
|
+
publishSyncProgress("project", {
|
|
62906
|
+
project: projectStats,
|
|
62907
|
+
currentProjectIndex
|
|
62908
|
+
});
|
|
62600
62909
|
} catch (cause) {
|
|
62601
62910
|
hadErrors = true;
|
|
62602
62911
|
yield* logWarning$1("failed to hydrate external Codex thread during foreground sync", {
|
|
@@ -62613,6 +62922,10 @@ const runExternalCodexThreadSync = gen(function* () {
|
|
|
62613
62922
|
nextCursor: firstPage.nextCursor,
|
|
62614
62923
|
sortKey: "updated_at"
|
|
62615
62924
|
});
|
|
62925
|
+
publishSyncProgress("project", {
|
|
62926
|
+
project: projectStats,
|
|
62927
|
+
currentProjectIndex
|
|
62928
|
+
});
|
|
62616
62929
|
}
|
|
62617
62930
|
} finally {
|
|
62618
62931
|
yield* tryPromise(() => lease.release()).pipe(ignoreCause({ log: true }));
|
|
@@ -62634,6 +62947,7 @@ const runExternalCodexThreadSync = gen(function* () {
|
|
|
62634
62947
|
bootstrappedProjectCount,
|
|
62635
62948
|
syncedProjectCount: syncTargets.length,
|
|
62636
62949
|
createdThreadCount,
|
|
62950
|
+
restoredThreadCount,
|
|
62637
62951
|
foregroundSummaryCount,
|
|
62638
62952
|
foregroundHydratedThreadCount,
|
|
62639
62953
|
foregroundImportedMessageCount,
|
|
@@ -62645,16 +62959,34 @@ const runExternalCodexThreadSync = gen(function* () {
|
|
|
62645
62959
|
syncMarkerVersion: importMarker?.version ?? null,
|
|
62646
62960
|
hadErrors
|
|
62647
62961
|
});
|
|
62962
|
+
publishSyncProgress(hadErrors ? "failed" : "completed", { finishedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
62648
62963
|
if (!hadErrors) yield* tryPromise(() => writeImportMarker(config.stateDir, {
|
|
62649
62964
|
version: EXTERNAL_SYNC_MARKER_VERSION,
|
|
62650
62965
|
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
62651
62966
|
bootstrappedProjectCount,
|
|
62652
62967
|
syncedProjectCount: syncTargets.length,
|
|
62653
62968
|
createdThreadCount,
|
|
62969
|
+
restoredThreadCount,
|
|
62654
62970
|
foregroundSummaryCount,
|
|
62655
62971
|
backgroundBackfillProjectCount: backgroundBackfillPlans.length
|
|
62656
62972
|
})).pipe(catch_((cause) => logWarning$1("failed to write external Codex thread sync marker", { cause })));
|
|
62973
|
+
return makeExternalThreadSyncStats({
|
|
62974
|
+
bootstrappedProjectCount,
|
|
62975
|
+
syncedProjectCount: syncTargets.length,
|
|
62976
|
+
createdThreadCount,
|
|
62977
|
+
restoredThreadCount,
|
|
62978
|
+
foregroundSummaryCount,
|
|
62979
|
+
foregroundHydratedThreadCount,
|
|
62980
|
+
foregroundImportedMessageCount,
|
|
62981
|
+
foregroundImportedActivityCount,
|
|
62982
|
+
foregroundImportedPlanCount,
|
|
62983
|
+
backgroundBackfillProjectCount: backgroundBackfillPlans.length,
|
|
62984
|
+
parityWorkspaceCount: importableParityWorkspaces.length,
|
|
62985
|
+
hadErrors
|
|
62986
|
+
});
|
|
62657
62987
|
});
|
|
62988
|
+
const runExternalCodexThreadSync = runExternalCodexThreadSyncWithOptions();
|
|
62989
|
+
const runExternalCodexThreadSyncForProject = (projectId) => runExternalCodexThreadSyncWithOptions({ projectId });
|
|
62658
62990
|
//#endregion
|
|
62659
62991
|
//#region src/orchestration/Layers/ThreadSnapshotPreparation.ts
|
|
62660
62992
|
function mapProviderSessionStatusToOrchestrationStatus(status) {
|
|
@@ -64868,7 +65200,7 @@ var RotatingFileSink = class {
|
|
|
64868
65200
|
this.maxBytes = options.maxBytes;
|
|
64869
65201
|
this.maxFiles = options.maxFiles;
|
|
64870
65202
|
this.throwOnError = options.throwOnError ?? false;
|
|
64871
|
-
fs.mkdirSync(
|
|
65203
|
+
fs.mkdirSync(nodePath.dirname(this.filePath), { recursive: true });
|
|
64872
65204
|
this.pruneOverflowBackups();
|
|
64873
65205
|
this.currentSize = this.readCurrentSize();
|
|
64874
65206
|
}
|
|
@@ -64903,13 +65235,13 @@ var RotatingFileSink = class {
|
|
|
64903
65235
|
}
|
|
64904
65236
|
pruneOverflowBackups() {
|
|
64905
65237
|
try {
|
|
64906
|
-
const dir =
|
|
64907
|
-
const baseName =
|
|
65238
|
+
const dir = nodePath.dirname(this.filePath);
|
|
65239
|
+
const baseName = nodePath.basename(this.filePath);
|
|
64908
65240
|
for (const entry of fs.readdirSync(dir)) {
|
|
64909
65241
|
if (!entry.startsWith(`${baseName}.`)) continue;
|
|
64910
65242
|
const suffix = Number(entry.slice(baseName.length + 1));
|
|
64911
65243
|
if (!Number.isInteger(suffix) || suffix <= this.maxFiles) continue;
|
|
64912
|
-
fs.rmSync(
|
|
65244
|
+
fs.rmSync(nodePath.join(dir, entry), { force: true });
|
|
64913
65245
|
}
|
|
64914
65246
|
} catch {
|
|
64915
65247
|
if (this.throwOnError) throw new Error(`Failed to prune log backups for ${this.filePath}`);
|
|
@@ -65049,7 +65381,7 @@ function makeEventNdjsonLogger(filePath, options) {
|
|
|
65049
65381
|
const streamLabel = resolveStreamLabel(options.stream);
|
|
65050
65382
|
const directoryReady = yield* sync(() => {
|
|
65051
65383
|
try {
|
|
65052
|
-
fs.mkdirSync(
|
|
65384
|
+
fs.mkdirSync(nodePath.dirname(filePath), { recursive: true });
|
|
65053
65385
|
return true;
|
|
65054
65386
|
} catch (error) {
|
|
65055
65387
|
return {
|
|
@@ -65072,7 +65404,7 @@ function makeEventNdjsonLogger(filePath, options) {
|
|
|
65072
65404
|
const existing = threadWriters.get(threadSegment);
|
|
65073
65405
|
if (existing) return existing;
|
|
65074
65406
|
const writer = yield* makeThreadWriter({
|
|
65075
|
-
filePath:
|
|
65407
|
+
filePath: nodePath.join(nodePath.dirname(filePath), `${threadSegment}.log`),
|
|
65076
65408
|
maxBytes,
|
|
65077
65409
|
maxFiles,
|
|
65078
65410
|
batchWindowMs,
|
|
@@ -65289,12 +65621,14 @@ function toUserInputQuestions(payload) {
|
|
|
65289
65621
|
const id = asString$3(question.id)?.trim();
|
|
65290
65622
|
const header = asString$3(question.header)?.trim();
|
|
65291
65623
|
const prompt = asString$3(question.question)?.trim();
|
|
65624
|
+
const multiSelect = question.multiSelect === true || question.multi_select === true;
|
|
65292
65625
|
if (!id || !header || !prompt || !options || options.length === 0) return;
|
|
65293
65626
|
return {
|
|
65294
65627
|
id,
|
|
65295
65628
|
header,
|
|
65296
65629
|
question: prompt,
|
|
65297
|
-
options
|
|
65630
|
+
options,
|
|
65631
|
+
multiSelect
|
|
65298
65632
|
};
|
|
65299
65633
|
}).filter((question) => question !== void 0);
|
|
65300
65634
|
return parsedQuestions.length > 0 ? parsedQuestions : void 0;
|
|
@@ -66596,7 +66930,7 @@ function normalizeShellCommand(value) {
|
|
|
66596
66930
|
}
|
|
66597
66931
|
function shellCandidateFromCommand(command) {
|
|
66598
66932
|
if (!command || command.length === 0) return null;
|
|
66599
|
-
const shellName =
|
|
66933
|
+
const shellName = nodePath.basename(command).toLowerCase();
|
|
66600
66934
|
if (process.platform !== "win32" && shellName === "zsh") return {
|
|
66601
66935
|
shell: command,
|
|
66602
66936
|
args: ["-o", "nopromptsp"]
|
|
@@ -66778,7 +67112,7 @@ var TerminalManagerRuntime = class extends EventEmitter {
|
|
|
66778
67112
|
this.subprocessPollInFlight = false;
|
|
66779
67113
|
this.killEscalationTimers = /* @__PURE__ */ new Map();
|
|
66780
67114
|
this.logger = createLogger("terminal");
|
|
66781
|
-
this.logsDir = options.logsDir ??
|
|
67115
|
+
this.logsDir = options.logsDir ?? nodePath.resolve(process.cwd(), ".logs", "terminals");
|
|
66782
67116
|
this.historyLineLimit = options.historyLineLimit ?? DEFAULT_HISTORY_LINE_LIMIT;
|
|
66783
67117
|
this.ptyAdapter = options.ptyAdapter;
|
|
66784
67118
|
this.shellResolver = options.shellResolver ?? defaultShellResolver;
|
|
@@ -67358,7 +67692,7 @@ var TerminalManagerRuntime = class extends EventEmitter {
|
|
|
67358
67692
|
async deleteAllHistoryForThread(threadId) {
|
|
67359
67693
|
const threadPrefix = `${toSafeThreadId(threadId)}_`;
|
|
67360
67694
|
try {
|
|
67361
|
-
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 }));
|
|
67362
67696
|
await Promise.all(removals);
|
|
67363
67697
|
} catch (error) {
|
|
67364
67698
|
this.logger.warn("failed to delete terminal histories for thread", {
|
|
@@ -67390,11 +67724,11 @@ var TerminalManagerRuntime = class extends EventEmitter {
|
|
|
67390
67724
|
}
|
|
67391
67725
|
historyPath(threadId, terminalId) {
|
|
67392
67726
|
const threadPart = toSafeThreadId(threadId);
|
|
67393
|
-
if (terminalId === "default") return
|
|
67394
|
-
return
|
|
67727
|
+
if (terminalId === "default") return nodePath.join(this.logsDir, `${threadPart}.log`);
|
|
67728
|
+
return nodePath.join(this.logsDir, `${threadPart}_${toSafeTerminalId(terminalId)}.log`);
|
|
67395
67729
|
}
|
|
67396
67730
|
legacyHistoryPath(threadId) {
|
|
67397
|
-
return
|
|
67731
|
+
return nodePath.join(this.logsDir, `${legacySafeThreadId(threadId)}.log`);
|
|
67398
67732
|
}
|
|
67399
67733
|
async runWithThreadLock(threadId, task) {
|
|
67400
67734
|
const previous = this.threadLocks.get(threadId) ?? Promise.resolve();
|
|
@@ -70370,8 +70704,8 @@ const NodePtyAdapterLive = effect(PtyAdapter, gen(function* () {
|
|
|
70370
70704
|
function makeServerProviderLayer() {
|
|
70371
70705
|
return gen(function* () {
|
|
70372
70706
|
const { stateDir } = yield* ServerConfig$1;
|
|
70373
|
-
const providerLogsDir =
|
|
70374
|
-
const providerEventLogPath =
|
|
70707
|
+
const providerLogsDir = nodePath.join(stateDir, "logs", "provider");
|
|
70708
|
+
const providerEventLogPath = nodePath.join(providerLogsDir, "events.log");
|
|
70375
70709
|
const nativeEventLogger = yield* makeEventNdjsonLogger(providerEventLogPath, { stream: "native" });
|
|
70376
70710
|
const canonicalEventLogger = yield* makeEventNdjsonLogger(providerEventLogPath, { stream: "canonical" });
|
|
70377
70711
|
const providerSessionDirectoryLayer = ProviderSessionDirectoryLive.pipe(provide$2(ProviderSessionRuntimeRepositoryLive));
|
|
@@ -72515,8 +72849,8 @@ function parseSchemaKeys(schemaRaw) {
|
|
|
72515
72849
|
async function readSchemaFromLocal() {
|
|
72516
72850
|
const candidates = [
|
|
72517
72851
|
process.env.CODEX_CONFIG_SCHEMA_PATH,
|
|
72518
|
-
|
|
72519
|
-
|
|
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")
|
|
72520
72854
|
].filter((candidate) => Boolean(candidate));
|
|
72521
72855
|
for (const candidate of candidates) try {
|
|
72522
72856
|
return await readFile(candidate, "utf8");
|
|
@@ -72614,21 +72948,6 @@ const DEFAULT_CONFIG_TEMPLATE = [
|
|
|
72614
72948
|
"web_search = \"cached\"",
|
|
72615
72949
|
""
|
|
72616
72950
|
].join("\n");
|
|
72617
|
-
const SUPPORTED_REASONING_EFFORT = new Set([
|
|
72618
|
-
"minimal",
|
|
72619
|
-
"low",
|
|
72620
|
-
"medium",
|
|
72621
|
-
"high",
|
|
72622
|
-
"xhigh",
|
|
72623
|
-
"none"
|
|
72624
|
-
]);
|
|
72625
|
-
const CLI_SUPPORTED_REASONING_EFFORT = new Set([
|
|
72626
|
-
"minimal",
|
|
72627
|
-
"low",
|
|
72628
|
-
"medium",
|
|
72629
|
-
"high",
|
|
72630
|
-
"none"
|
|
72631
|
-
]);
|
|
72632
72951
|
const YOLO_PRESET = {
|
|
72633
72952
|
model: "gpt-5.3-codex",
|
|
72634
72953
|
reviewModel: "gpt-5.3-codex",
|
|
@@ -72654,10 +72973,10 @@ const YOLO_PRESET = {
|
|
|
72654
72973
|
//#endregion
|
|
72655
72974
|
//#region ../../packages/runtime-codex/src/codex/config-io.ts
|
|
72656
72975
|
function getConfigPath(options) {
|
|
72657
|
-
return
|
|
72976
|
+
return nodePath.join(resolveCodexHomeDir(options), "config.toml");
|
|
72658
72977
|
}
|
|
72659
72978
|
async function ensureConfigDirExists(filePath) {
|
|
72660
|
-
await mkdir(
|
|
72979
|
+
await mkdir(nodePath.dirname(filePath), { recursive: true });
|
|
72661
72980
|
}
|
|
72662
72981
|
function normalizeLineEndings(content) {
|
|
72663
72982
|
return content.replace(/\r\n/g, "\n");
|
|
@@ -72921,14 +73240,6 @@ function buildSettingsSummary(data) {
|
|
|
72921
73240
|
}
|
|
72922
73241
|
};
|
|
72923
73242
|
}
|
|
72924
|
-
function getUnsupportedReasoningEffort(data, allowed = SUPPORTED_REASONING_EFFORT) {
|
|
72925
|
-
if (!data || !Object.prototype.hasOwnProperty.call(data, "model_reasoning_effort")) return null;
|
|
72926
|
-
const raw = data["model_reasoning_effort"];
|
|
72927
|
-
if (typeof raw !== "string") return String(raw ?? "");
|
|
72928
|
-
const value = raw.trim();
|
|
72929
|
-
if (!value) return null;
|
|
72930
|
-
return allowed.has(value) ? null : value;
|
|
72931
|
-
}
|
|
72932
73243
|
function parseRmcpClientEnabled(data) {
|
|
72933
73244
|
const rawFeatures = data && typeof data["features"] === "object" && data["features"] !== null && !Array.isArray(data["features"]) ? data["features"] : null;
|
|
72934
73245
|
if (rawFeatures && typeof rawFeatures["rmcp_client"] === "boolean") return rawFeatures["rmcp_client"];
|
|
@@ -73232,21 +73543,6 @@ async function updateCodexConfigValues(updates, options) {
|
|
|
73232
73543
|
await writeConfigContent(serialized, options);
|
|
73233
73544
|
return buildSnapshot(serialized, true, options);
|
|
73234
73545
|
}
|
|
73235
|
-
async function sanitizeCodexConfigForCli(options) {
|
|
73236
|
-
const { content } = await readConfigContent(options);
|
|
73237
|
-
const parsed = tryParseToml(content);
|
|
73238
|
-
if (!parsed.data) throw new Error(parsed.error ?? "config.toml contains invalid TOML syntax.");
|
|
73239
|
-
if (!getUnsupportedReasoningEffort(parsed.data, CLI_SUPPORTED_REASONING_EFFORT)) return null;
|
|
73240
|
-
const sanitized = { ...parsed.data };
|
|
73241
|
-
delete sanitized["model_reasoning_effort"];
|
|
73242
|
-
await writeConfigContent(ensureTrailingNewline((0, import_toml.stringify)(sanitized)), options);
|
|
73243
|
-
let restored = false;
|
|
73244
|
-
return async () => {
|
|
73245
|
-
if (restored) return;
|
|
73246
|
-
restored = true;
|
|
73247
|
-
await writeConfigContent(ensureTrailingNewline(normalizeLineEndings(content)), options);
|
|
73248
|
-
};
|
|
73249
|
-
}
|
|
73250
73546
|
//#endregion
|
|
73251
73547
|
//#region ../../packages/contracts/src/profiles/identity.ts
|
|
73252
73548
|
function normalizeValue(value) {
|
|
@@ -73429,10 +73725,10 @@ function parseRateLimitSnapshotFromRpcMessage(message) {
|
|
|
73429
73725
|
}
|
|
73430
73726
|
async function fetchRateLimitsViaRpc(envOverride, options = {}) {
|
|
73431
73727
|
const binaryPath = options.codexPath ?? await requireCodexCli();
|
|
73432
|
-
const tempHome = await promises.mkdtemp(
|
|
73433
|
-
const tempAuthPath =
|
|
73728
|
+
const tempHome = await promises.mkdtemp(nodePath.join(nodeOs.tmpdir(), "codex-rpc-"));
|
|
73729
|
+
const tempAuthPath = nodePath.join(tempHome, "auth.json");
|
|
73434
73730
|
let initialSourceAuth = null;
|
|
73435
|
-
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"));
|
|
73436
73732
|
try {
|
|
73437
73733
|
initialSourceAuth = await promises.readFile(sourceAuthPath, "utf8").catch(() => null);
|
|
73438
73734
|
if (!initialSourceAuth) return null;
|
|
@@ -73581,21 +73877,6 @@ var ProfileManager = class {
|
|
|
73581
73877
|
isNotFoundError(error) {
|
|
73582
73878
|
return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
|
|
73583
73879
|
}
|
|
73584
|
-
async readPreferredProfileName() {
|
|
73585
|
-
try {
|
|
73586
|
-
return await getLastProfileName();
|
|
73587
|
-
} catch (error) {
|
|
73588
|
-
logWarn("Failed to read preferred profile name:", error);
|
|
73589
|
-
return null;
|
|
73590
|
-
}
|
|
73591
|
-
}
|
|
73592
|
-
async persistPreferredProfileName(name) {
|
|
73593
|
-
try {
|
|
73594
|
-
await persistLastProfileName(name);
|
|
73595
|
-
} catch (error) {
|
|
73596
|
-
logWarn("Failed to persist preferred profile name:", error);
|
|
73597
|
-
}
|
|
73598
|
-
}
|
|
73599
73880
|
toStateRecord(record) {
|
|
73600
73881
|
return {
|
|
73601
73882
|
name: record.name,
|
|
@@ -73694,44 +73975,33 @@ var ProfileManager = class {
|
|
|
73694
73975
|
return null;
|
|
73695
73976
|
}
|
|
73696
73977
|
}
|
|
73697
|
-
|
|
73698
|
-
|
|
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) {
|
|
73699
73990
|
let raw;
|
|
73700
73991
|
try {
|
|
73701
|
-
raw = await promises$1.readFile(
|
|
73992
|
+
raw = await promises$1.readFile(authPath, "utf8");
|
|
73702
73993
|
} catch (error) {
|
|
73703
|
-
if (this.isNotFoundError(error)) return
|
|
73704
|
-
fingerprint: null,
|
|
73705
|
-
email: null,
|
|
73706
|
-
accountId: null,
|
|
73707
|
-
userId: null,
|
|
73708
|
-
chatgptUserId: null,
|
|
73709
|
-
workspaceId: null
|
|
73710
|
-
};
|
|
73994
|
+
if (this.isNotFoundError(error)) return this.emptyAuthSnapshot();
|
|
73711
73995
|
const message = error instanceof Error ? error.message : "unknown error";
|
|
73712
|
-
const signature = typeof message === "string" ? `${message}:${
|
|
73996
|
+
const signature = typeof message === "string" ? `${message}:${authPath}` : authPath;
|
|
73713
73997
|
if (this.lastActiveAuthErrorSignature !== signature) {
|
|
73714
|
-
logWarn("Failed to snapshot
|
|
73998
|
+
logWarn("Failed to snapshot auth file:", error);
|
|
73715
73999
|
this.lastActiveAuthErrorSignature = signature;
|
|
73716
74000
|
}
|
|
73717
|
-
return
|
|
73718
|
-
fingerprint: null,
|
|
73719
|
-
email: null,
|
|
73720
|
-
accountId: null,
|
|
73721
|
-
userId: null,
|
|
73722
|
-
chatgptUserId: null,
|
|
73723
|
-
workspaceId: null
|
|
73724
|
-
};
|
|
74001
|
+
return this.emptyAuthSnapshot();
|
|
73725
74002
|
}
|
|
73726
74003
|
const trimmed = raw.trim();
|
|
73727
|
-
if (!trimmed) return
|
|
73728
|
-
fingerprint: null,
|
|
73729
|
-
email: null,
|
|
73730
|
-
accountId: null,
|
|
73731
|
-
userId: null,
|
|
73732
|
-
chatgptUserId: null,
|
|
73733
|
-
workspaceId: null
|
|
73734
|
-
};
|
|
74004
|
+
if (!trimmed) return this.emptyAuthSnapshot();
|
|
73735
74005
|
const fingerprint = createHash("sha256").update(trimmed).digest("hex");
|
|
73736
74006
|
try {
|
|
73737
74007
|
const parsed = JSON.parse(trimmed);
|
|
@@ -73744,19 +74014,25 @@ var ProfileManager = class {
|
|
|
73744
74014
|
accountId: this.getAccountIdFromData(normalized) ?? null,
|
|
73745
74015
|
userId: metadata?.userId ?? null,
|
|
73746
74016
|
chatgptUserId: metadata?.chatgptUserId ?? null,
|
|
74017
|
+
chatgptAccountUserId: metadata?.chatgptAccountUserId ?? null,
|
|
73747
74018
|
workspaceId: workspace.id ?? null
|
|
73748
74019
|
};
|
|
73749
74020
|
} catch {
|
|
73750
|
-
return
|
|
73751
|
-
fingerprint,
|
|
73752
|
-
email: null,
|
|
73753
|
-
accountId: null,
|
|
73754
|
-
userId: null,
|
|
73755
|
-
chatgptUserId: null,
|
|
73756
|
-
workspaceId: null
|
|
73757
|
-
};
|
|
74021
|
+
return this.emptyAuthSnapshot(fingerprint);
|
|
73758
74022
|
}
|
|
73759
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
|
+
}
|
|
73760
74036
|
/**
|
|
73761
74037
|
* Decode a JWT payload into an object without validating the signature.
|
|
73762
74038
|
*/
|
|
@@ -74017,7 +74293,7 @@ var ProfileManager = class {
|
|
|
74017
74293
|
}
|
|
74018
74294
|
async writeAtomic(filePath, contents) {
|
|
74019
74295
|
const tempPath = `${filePath}.tmp`;
|
|
74020
|
-
const dir = path
|
|
74296
|
+
const dir = path.dirname(filePath);
|
|
74021
74297
|
await promises$1.mkdir(dir, { recursive: true });
|
|
74022
74298
|
await promises$1.writeFile(tempPath, contents, "utf8");
|
|
74023
74299
|
try {
|
|
@@ -74072,6 +74348,99 @@ var ProfileManager = class {
|
|
|
74072
74348
|
metadata: record.metadata
|
|
74073
74349
|
}) ?? resolveFallbackAccountKey(record.name);
|
|
74074
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
|
+
}
|
|
74075
74444
|
resolveWorkspaceIdentity(data, metadata) {
|
|
74076
74445
|
const directId = "workspace_id" in data && typeof data.workspace_id === "string" ? data.workspace_id.trim() : void 0;
|
|
74077
74446
|
const directName = "workspace_name" in data && typeof data.workspace_name === "string" ? data.workspace_name.trim() : void 0;
|
|
@@ -74096,15 +74465,6 @@ var ProfileManager = class {
|
|
|
74096
74465
|
const normalized = this.normalizeProfileName(name);
|
|
74097
74466
|
return await this.readProfileRecord(normalized) ?? void 0;
|
|
74098
74467
|
}
|
|
74099
|
-
async getProfileRowByIdentityKey(identityKey, options) {
|
|
74100
|
-
const matches = (await this.listProfileRecords()).filter((record) => this.resolveProfileIdentityFromRecord(record) === identityKey);
|
|
74101
|
-
if (matches.length === 0) return;
|
|
74102
|
-
const mustMatch = typeof options?.authMethod === "string" ? options.authMethod.trim().toLowerCase() : null;
|
|
74103
|
-
if (mustMatch) return matches.find((record) => this.resolveAuthMethod(record.data) === mustMatch);
|
|
74104
|
-
const prefer = typeof options?.preferAuthMethod === "string" ? options.preferAuthMethod.trim().toLowerCase() : null;
|
|
74105
|
-
if (prefer) return matches.find((record) => this.resolveAuthMethod(record.data) === prefer) ?? matches[0];
|
|
74106
|
-
return matches[0];
|
|
74107
|
-
}
|
|
74108
74468
|
async getProfileRowByIdentityAndWorkspace(identityKey, workspaceId, options) {
|
|
74109
74469
|
const workspaceKey = workspaceId && workspaceId.trim().length > 0 ? workspaceId.trim() : DEFAULT_WORKSPACE_ID;
|
|
74110
74470
|
const matches = (await this.listProfileRecords()).filter((record) => {
|
|
@@ -74119,6 +74479,9 @@ var ProfileManager = class {
|
|
|
74119
74479
|
if (prefer) return matches.find((record) => this.resolveAuthMethod(record.data) === prefer) ?? matches[0];
|
|
74120
74480
|
return matches[0];
|
|
74121
74481
|
}
|
|
74482
|
+
normalizeWorkspaceId(value) {
|
|
74483
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : DEFAULT_WORKSPACE_ID;
|
|
74484
|
+
}
|
|
74122
74485
|
async persistProfileRecord(name, data, metadata, options) {
|
|
74123
74486
|
const resolvedName = this.normalizeProfileName(name);
|
|
74124
74487
|
const existingRecord = await this.readProfileRecord(resolvedName);
|
|
@@ -74166,6 +74529,23 @@ var ProfileManager = class {
|
|
|
74166
74529
|
metadata: row.metadata
|
|
74167
74530
|
});
|
|
74168
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
|
+
}
|
|
74169
74549
|
buildProfileFromData(name, data, fallback) {
|
|
74170
74550
|
const metadata = fallback?.metadata ?? this.extractProfileMetadata(data);
|
|
74171
74551
|
const workspace = this.resolveWorkspaceIdentity(data, metadata);
|
|
@@ -74217,6 +74597,12 @@ var ProfileManager = class {
|
|
|
74217
74597
|
}
|
|
74218
74598
|
};
|
|
74219
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;
|
|
74220
74606
|
return codexAuth;
|
|
74221
74607
|
}
|
|
74222
74608
|
/**
|
|
@@ -74235,7 +74621,12 @@ var ProfileManager = class {
|
|
|
74235
74621
|
const rootLastRefresh = typeof data.last_refresh === "string" ? data.last_refresh.trim() : "";
|
|
74236
74622
|
const normalizedLastRefresh = tokenLastRefresh || rootLastRefresh;
|
|
74237
74623
|
if (normalizedLastRefresh) profile.last_refresh = normalizedLastRefresh;
|
|
74238
|
-
|
|
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;
|
|
74239
74630
|
return profile;
|
|
74240
74631
|
}
|
|
74241
74632
|
mergeProfileRecords(existing, incoming) {
|
|
@@ -74337,6 +74728,10 @@ var ProfileManager = class {
|
|
|
74337
74728
|
logWarn(`Skipped syncing tokens for profile '${profileName}' because Codex auth switched to a different OpenAI identity.`);
|
|
74338
74729
|
return;
|
|
74339
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
|
+
}
|
|
74340
74735
|
if (!this.hasTokenChanges(existing, normalized)) return;
|
|
74341
74736
|
const { profile: merged, metadata: mergedMetadata } = this.mergeProfileRecords(existing, normalized);
|
|
74342
74737
|
try {
|
|
@@ -74437,7 +74832,7 @@ var ProfileManager = class {
|
|
|
74437
74832
|
if (profileData.auth_method && profileData.auth_method !== "codex-cli") throw new Error("Unsupported auth method. Codex CLI profiles only.");
|
|
74438
74833
|
const codexFormat = this.convertToCodexFormat(profileData);
|
|
74439
74834
|
const profileHome = await this.prepareProfileHome(profileName, codexFormat);
|
|
74440
|
-
const authPath = path
|
|
74835
|
+
const authPath = path.join(profileHome, "auth.json");
|
|
74441
74836
|
const envOverrides = {
|
|
74442
74837
|
...process.env,
|
|
74443
74838
|
HOME: profileHome,
|
|
@@ -74491,7 +74886,7 @@ var ProfileManager = class {
|
|
|
74491
74886
|
const profileName = this.normalizeProfileName(name);
|
|
74492
74887
|
const record = await this.getProfileRowByName(profileName);
|
|
74493
74888
|
if (!record) throw new Error(`Profile '${profileName}' not found!`);
|
|
74494
|
-
const authPath = path
|
|
74889
|
+
const authPath = path.join(profileHome ?? this.getProfileHomePath(profileName), "auth.json");
|
|
74495
74890
|
let finalAuthContent = null;
|
|
74496
74891
|
try {
|
|
74497
74892
|
finalAuthContent = await promises$1.readFile(authPath, "utf8");
|
|
@@ -74500,79 +74895,70 @@ var ProfileManager = class {
|
|
|
74500
74895
|
}
|
|
74501
74896
|
if (finalAuthContent) {
|
|
74502
74897
|
await this.syncProfileTokensFromAuthContent(profileName, record.data, finalAuthContent);
|
|
74898
|
+
await this.syncActiveAuthFromProfileIfCurrent(profileName);
|
|
74503
74899
|
return;
|
|
74504
74900
|
}
|
|
74505
74901
|
await this.syncProfileTokensFromActiveAuth(profileName, record.data, authPath);
|
|
74902
|
+
await this.syncActiveAuthFromProfileIfCurrent(profileName);
|
|
74506
74903
|
}));
|
|
74507
74904
|
}
|
|
74508
74905
|
/**
|
|
74509
74906
|
* Create a new profile by running codex login
|
|
74510
74907
|
*/
|
|
74511
|
-
async
|
|
74512
|
-
|
|
74513
|
-
const profileName = this.normalizeProfileName(name);
|
|
74514
|
-
if (await this.getProfileRowByName(profileName)) throw new Error(`Profile '${profileName}' already exists!`);
|
|
74515
|
-
let authData;
|
|
74908
|
+
async readAuthFileForImport(authPath, actionLabel) {
|
|
74909
|
+
let authRaw;
|
|
74516
74910
|
try {
|
|
74517
|
-
|
|
74518
|
-
authData = JSON.parse(authRaw);
|
|
74911
|
+
authRaw = await promises$1.readFile(authPath, "utf8");
|
|
74519
74912
|
} catch (error) {
|
|
74520
|
-
|
|
74521
|
-
|
|
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.");
|
|
74522
74916
|
}
|
|
74523
|
-
const normalizedProfile = this.normalizeProfileData(authData);
|
|
74524
|
-
normalizedProfile.created_at = normalizedProfile.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
74525
|
-
if (typeof normalizedProfile.auth_method === "string") normalizedProfile.auth_method = normalizedProfile.auth_method.trim().toLowerCase();
|
|
74526
|
-
normalizedProfile.auth_method = normalizedProfile.auth_method ?? "codex-cli";
|
|
74527
|
-
const metadata = this.extractProfileMetadata(normalizedProfile);
|
|
74528
|
-
const workspace = this.resolveWorkspaceIdentity(normalizedProfile, metadata);
|
|
74529
|
-
normalizedProfile.workspace_id = normalizedProfile.workspace_id ?? workspace.id ?? DEFAULT_WORKSPACE_ID;
|
|
74530
|
-
if (workspace.name) normalizedProfile.workspace_name = normalizedProfile.workspace_name ?? workspace.name;
|
|
74531
|
-
const resolvedEmail = this.resolveProfileEmail(normalizedProfile, metadata);
|
|
74532
|
-
if (resolvedEmail) normalizedProfile.email = resolvedEmail;
|
|
74533
74917
|
try {
|
|
74534
|
-
|
|
74535
|
-
const stored = await this.getProfileRowByName(profileName);
|
|
74536
|
-
return stored ? this.buildProfileFromRow(stored) : null;
|
|
74918
|
+
return JSON.parse(authRaw);
|
|
74537
74919
|
} catch (error) {
|
|
74538
|
-
logError(
|
|
74539
|
-
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.");
|
|
74540
74922
|
}
|
|
74541
74923
|
}
|
|
74542
|
-
|
|
74543
|
-
|
|
74544
|
-
|
|
74545
|
-
|
|
74546
|
-
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) {
|
|
74547
74933
|
const profileName = this.normalizeProfileName(name);
|
|
74548
|
-
|
|
74549
|
-
|
|
74550
|
-
|
|
74551
|
-
|
|
74552
|
-
|
|
74553
|
-
|
|
74554
|
-
|
|
74555
|
-
|
|
74556
|
-
|
|
74557
|
-
|
|
74558
|
-
|
|
74559
|
-
}
|
|
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
|
+
}));
|
|
74560
74945
|
}
|
|
74561
|
-
async
|
|
74562
|
-
await this.initialize();
|
|
74946
|
+
async createProfileFromAuthFile(name, authPath) {
|
|
74563
74947
|
const profileName = this.normalizeProfileName(name);
|
|
74564
|
-
|
|
74565
|
-
|
|
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) {
|
|
74566
74961
|
const existing = record.data;
|
|
74567
|
-
let activeAuth;
|
|
74568
|
-
try {
|
|
74569
|
-
const authContent = await promises$1.readFile(this.activeAuth, "utf8");
|
|
74570
|
-
activeAuth = JSON.parse(authContent);
|
|
74571
|
-
} catch (error) {
|
|
74572
|
-
if (this.isNotFoundError(error)) throw new Error("Codex CLI did not produce an auth file. Complete the login before refreshing this profile.");
|
|
74573
|
-
logError("Failed to read Codex auth file during refresh:", error);
|
|
74574
|
-
throw new Error("Failed to read Codex auth file. Complete the login and try again.");
|
|
74575
|
-
}
|
|
74576
74962
|
const normalized = this.normalizeProfileData(activeAuth);
|
|
74577
74963
|
const existingMetadata = record.metadata ?? this.extractProfileMetadata(existing);
|
|
74578
74964
|
const currentWorkspace = this.resolveWorkspaceIdentity(existing, existingMetadata);
|
|
@@ -74605,15 +74991,72 @@ var ProfileManager = class {
|
|
|
74605
74991
|
return profile;
|
|
74606
74992
|
}
|
|
74607
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
|
+
/**
|
|
74608
75036
|
* Execute an action with a profile's auth injected.
|
|
74609
75037
|
* Creates an isolated CODEX_HOME with the profile's auth/config, invokes the action
|
|
74610
75038
|
* with env overrides, then syncs any refreshed tokens back to the profile.
|
|
74611
75039
|
*/
|
|
74612
75040
|
async runWithProfileAuth(name, action) {
|
|
74613
|
-
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
|
+
}));
|
|
74614
75048
|
}
|
|
74615
75049
|
async readLiveRateLimits(name, options = {}) {
|
|
74616
|
-
|
|
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
|
+
}
|
|
74617
75060
|
}
|
|
74618
75061
|
/**
|
|
74619
75062
|
* Rename a profile
|
|
@@ -74625,8 +75068,6 @@ var ProfileManager = class {
|
|
|
74625
75068
|
const existing = await this.getProfileRowByName(sourceName);
|
|
74626
75069
|
if (!existing) throw new Error(`Profile '${sourceName}' not found!`);
|
|
74627
75070
|
if (await this.getProfileRowByName(targetName)) throw new Error(`Profile '${targetName}' already exists!`);
|
|
74628
|
-
const preferred = await this.readPreferredProfileName();
|
|
74629
|
-
if (preferred && preferred === sourceName) await this.persistPreferredProfileName(targetName);
|
|
74630
75071
|
const oldHome = this.getProfileHomePath(sourceName);
|
|
74631
75072
|
const newHome = this.getProfileHomePath(targetName);
|
|
74632
75073
|
try {
|
|
@@ -74683,24 +75124,37 @@ var ProfileManager = class {
|
|
|
74683
75124
|
const profileName = this.normalizeProfileName(name);
|
|
74684
75125
|
const record = await this.getProfileRowByName(profileName);
|
|
74685
75126
|
if (!record) throw new Error(`Profile '${profileName}' not found!`);
|
|
74686
|
-
|
|
74687
|
-
const dashboardState = await this.readProfileDashboardState();
|
|
74688
|
-
delete dashboardState.customGroupsByAccountKey[resolveFallbackAccountKey(profileName)];
|
|
74689
|
-
await this.writeProfileDashboardState(dashboardState);
|
|
74690
|
-
}
|
|
74691
|
-
const preferred = await this.readPreferredProfileName();
|
|
74692
|
-
if (preferred && preferred === profileName) await this.persistPreferredProfileName(null);
|
|
74693
|
-
try {
|
|
74694
|
-
await promises$1.rm(this.getProfileHomePath(profileName), {
|
|
74695
|
-
recursive: true,
|
|
74696
|
-
force: true
|
|
74697
|
-
});
|
|
74698
|
-
} catch (error) {
|
|
74699
|
-
if (!this.isNotFoundError(error)) logWarn(`Failed to remove profile home for '${profileName}':`, error);
|
|
74700
|
-
}
|
|
74701
|
-
await this.deleteProfileRecord(profileName);
|
|
75127
|
+
await this.deleteProfileRecordAndHome(record);
|
|
74702
75128
|
return true;
|
|
74703
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
|
+
}
|
|
74704
75158
|
/**
|
|
74705
75159
|
* Delete every profile that does not appear in the allow-list.
|
|
74706
75160
|
* Used to enforce plan limits when local storage was tampered with.
|
|
@@ -74730,67 +75184,46 @@ var ProfileManager = class {
|
|
|
74730
75184
|
} catch (error) {
|
|
74731
75185
|
if (!this.isNotFoundError(error)) logWarn(`Failed to remove profile '${name}' during plan enforcement:`, error);
|
|
74732
75186
|
}
|
|
74733
|
-
const preferred = await this.readPreferredProfileName();
|
|
74734
|
-
if (preferred && removed.includes(preferred)) await this.persistPreferredProfileName(null);
|
|
74735
75187
|
return removed;
|
|
74736
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
|
+
}
|
|
74737
75218
|
/**
|
|
74738
|
-
* Check if the
|
|
75219
|
+
* Check if the real Codex auth file matches a saved profile.
|
|
74739
75220
|
*/
|
|
74740
75221
|
async getCurrentProfile() {
|
|
74741
|
-
await this.
|
|
74742
|
-
|
|
74743
|
-
|
|
74744
|
-
|
|
74745
|
-
|
|
74746
|
-
name: normalizedPreferred,
|
|
74747
|
-
trusted: true
|
|
74748
|
-
};
|
|
74749
|
-
} catch {}
|
|
74750
|
-
const activeAuth = await this.readActiveAuthFile();
|
|
74751
|
-
if (activeAuth) {
|
|
74752
|
-
const normalizedAuth = this.normalizeProfileData(activeAuth);
|
|
74753
|
-
const authMetadata = this.extractProfileMetadata(normalizedAuth);
|
|
74754
|
-
const workspace = this.resolveWorkspaceIdentity(normalizedAuth, authMetadata);
|
|
74755
|
-
const activeIdentityKey = this.resolveProfileIdentityFromData(null, normalizedAuth, authMetadata);
|
|
74756
|
-
if (activeIdentityKey) {
|
|
74757
|
-
const scoped = await this.getProfileRowByIdentityAndWorkspace(activeIdentityKey, workspace.id ?? DEFAULT_WORKSPACE_ID, { preferAuthMethod: "codex-cli" });
|
|
74758
|
-
if (scoped) {
|
|
74759
|
-
const normalized = this.normalizeProfileName(scoped.name);
|
|
74760
|
-
await this.persistPreferredProfileName(normalized);
|
|
74761
|
-
return {
|
|
74762
|
-
name: normalized,
|
|
74763
|
-
trusted: true
|
|
74764
|
-
};
|
|
74765
|
-
}
|
|
74766
|
-
const matching = await this.getProfileRowByIdentityKey(activeIdentityKey, { preferAuthMethod: "codex-cli" });
|
|
74767
|
-
if (matching) {
|
|
74768
|
-
const normalized = this.normalizeProfileName(matching.name);
|
|
74769
|
-
await this.persistPreferredProfileName(normalized);
|
|
74770
|
-
return {
|
|
74771
|
-
name: normalized,
|
|
74772
|
-
trusted: true
|
|
74773
|
-
};
|
|
74774
|
-
}
|
|
74775
|
-
}
|
|
74776
|
-
}
|
|
74777
|
-
const preferred = await this.readPreferredProfileName();
|
|
74778
|
-
if (preferred) {
|
|
74779
|
-
try {
|
|
74780
|
-
const normalized = this.normalizeProfileName(preferred);
|
|
74781
|
-
if (await this.getProfileRowByName(normalized)) return {
|
|
74782
|
-
name: normalized,
|
|
74783
|
-
trusted: true
|
|
74784
|
-
};
|
|
74785
|
-
} catch {
|
|
74786
|
-
await this.persistPreferredProfileName(null);
|
|
74787
|
-
return {
|
|
74788
|
-
name: null,
|
|
74789
|
-
trusted: false
|
|
74790
|
-
};
|
|
74791
|
-
}
|
|
74792
|
-
await this.persistPreferredProfileName(null);
|
|
74793
|
-
}
|
|
75222
|
+
const status = await this.getActiveCodexAuthStatus({ autoImport: false });
|
|
75223
|
+
if (status.state === "matched" && status.profileName) return {
|
|
75224
|
+
name: status.profileName,
|
|
75225
|
+
trusted: true
|
|
75226
|
+
};
|
|
74794
75227
|
return {
|
|
74795
75228
|
name: null,
|
|
74796
75229
|
trusted: false
|
|
@@ -74882,13 +75315,6 @@ var ProfileManager = class {
|
|
|
74882
75315
|
if (!this.isNotFoundError(error)) logWarn(`Failed to remove profile '${name}' during cloud sync replace:`, error);
|
|
74883
75316
|
}
|
|
74884
75317
|
}
|
|
74885
|
-
const preferred = await this.readPreferredProfileName();
|
|
74886
|
-
if (preferred) try {
|
|
74887
|
-
const normalizedPreferred = this.normalizeProfileName(preferred);
|
|
74888
|
-
if (!normalized.has(normalizedPreferred)) await this.persistPreferredProfileName(null);
|
|
74889
|
-
} catch {
|
|
74890
|
-
await this.persistPreferredProfileName(null);
|
|
74891
|
-
}
|
|
74892
75318
|
return {
|
|
74893
75319
|
imported: normalized.size,
|
|
74894
75320
|
removed
|
|
@@ -74912,6 +75338,58 @@ async function loadCachedRateLimitSnapshots(keys) {
|
|
|
74912
75338
|
return results;
|
|
74913
75339
|
}
|
|
74914
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
|
|
74915
75393
|
//#region ../../packages/runtime-profiles/src/profiles/rate-limit-probe.ts
|
|
74916
75394
|
const MIN_REFRESH_INTERVAL_MS = 3 * 6e4;
|
|
74917
75395
|
const DEFAULT_REFRESH_INTERVAL_MS = 3 * 6e4;
|
|
@@ -75305,7 +75783,8 @@ async function executeAccountRefresh(accountId, state, options = {}) {
|
|
|
75305
75783
|
async function bootstrapInitialRefresh() {
|
|
75306
75784
|
try {
|
|
75307
75785
|
await ensureProfileManagerReady();
|
|
75308
|
-
|
|
75786
|
+
const { profiles } = await loadLicenseVisibleProfiles(sharedProfileManager);
|
|
75787
|
+
await refreshRateLimitTelemetry(sharedProfileManager, profiles, /* @__PURE__ */ new Map(), { enqueueAll: true });
|
|
75309
75788
|
emitRefreshState();
|
|
75310
75789
|
} catch (error) {
|
|
75311
75790
|
logError("Failed to bootstrap rate-limit refresh queue:", error);
|
|
@@ -75342,66 +75821,67 @@ function profileKey(profile) {
|
|
|
75342
75821
|
}
|
|
75343
75822
|
/**
|
|
75344
75823
|
* Loads profiles and related telemetry for the dashboard.
|
|
75345
|
-
* When
|
|
75346
|
-
*
|
|
75824
|
+
* When a free user has more saved profiles than allowed, extra profiles are hidden,
|
|
75825
|
+
* not deleted.
|
|
75347
75826
|
*/
|
|
75348
75827
|
async function loadProfilesViewData(options = {}) {
|
|
75349
75828
|
const backgroundRefresh = options.backgroundRefresh !== false;
|
|
75350
75829
|
await profileManager.initialize();
|
|
75351
75830
|
const license = await licenseService.getCachedStatus();
|
|
75352
75831
|
if (backgroundRefresh) licenseService.refreshStatusInBackground();
|
|
75353
|
-
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);
|
|
75354
75845
|
const profiles = await profileManager.listProfiles();
|
|
75355
75846
|
const customGroupsByAccountKey = await profileManager.getCustomGroupsByAccountKey();
|
|
75356
|
-
const
|
|
75357
|
-
|
|
75358
|
-
|
|
75359
|
-
|
|
75360
|
-
|
|
75361
|
-
|
|
75362
|
-
|
|
75363
|
-
|
|
75364
|
-
|
|
75365
|
-
|
|
75366
|
-
|
|
75367
|
-
|
|
75368
|
-
|
|
75369
|
-
|
|
75370
|
-
|
|
75371
|
-
|
|
75372
|
-
|
|
75373
|
-
|
|
75374
|
-
|
|
75375
|
-
|
|
75376
|
-
|
|
75377
|
-
|
|
75378
|
-
|
|
75379
|
-
|
|
75380
|
-
|
|
75381
|
-
|
|
75382
|
-
|
|
75383
|
-
profileName: issue.profileName,
|
|
75384
|
-
message: issue.message,
|
|
75385
|
-
code: issue.code,
|
|
75386
|
-
observedAt: issue.observedAt ? new Date(issue.observedAt).toISOString() : void 0
|
|
75387
|
-
}))
|
|
75388
|
-
};
|
|
75389
|
-
const snapshotMap = resolvedSnapshots;
|
|
75390
|
-
enrichedProfiles = effectiveProfiles.map((profile) => ({
|
|
75391
|
-
...profile,
|
|
75392
|
-
rateLimit: snapshotMap.get(profileKey(profile)) ?? null
|
|
75393
|
-
}));
|
|
75394
|
-
} else enrichedProfiles = effectiveProfiles.map((profile) => ({
|
|
75395
|
-
...profile,
|
|
75396
|
-
rateLimit: null
|
|
75397
|
-
}));
|
|
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
|
+
};
|
|
75398
75874
|
return {
|
|
75399
|
-
profiles:
|
|
75875
|
+
profiles: effectiveProfiles.map((profile) => ({
|
|
75876
|
+
...profile,
|
|
75877
|
+
rateLimit: resolvedSnapshots.get(profileKey(profile)) ?? null
|
|
75878
|
+
})),
|
|
75400
75879
|
currentProfile,
|
|
75401
75880
|
currentProfileTrusted,
|
|
75402
75881
|
refreshStatus,
|
|
75403
|
-
license: licenseWithCounts,
|
|
75404
|
-
customGroupsByAccountKey
|
|
75882
|
+
license: visible.licenseWithCounts,
|
|
75883
|
+
customGroupsByAccountKey,
|
|
75884
|
+
activeCodexAuth
|
|
75405
75885
|
};
|
|
75406
75886
|
}
|
|
75407
75887
|
async function resolveRateLimitSnapshots(accountIds) {
|
|
@@ -75409,34 +75889,6 @@ async function resolveRateLimitSnapshots(accountIds) {
|
|
|
75409
75889
|
if (uniqueAccountIds.length === 0) return /* @__PURE__ */ new Map();
|
|
75410
75890
|
return await loadCachedRateLimitSnapshots(uniqueAccountIds);
|
|
75411
75891
|
}
|
|
75412
|
-
function resolveVisibleProfiles(profiles, license, currentProfile) {
|
|
75413
|
-
if (license.isPro) return profiles;
|
|
75414
|
-
const limit = typeof license.profileLimit === "number" && license.profileLimit >= 0 ? license.profileLimit : 2;
|
|
75415
|
-
if (profiles.length <= limit) return profiles;
|
|
75416
|
-
const normalizedCurrent = currentProfile ?? null;
|
|
75417
|
-
return profiles.slice().sort((a, b) => compareProfilesForLimit(a, b, normalizedCurrent)).slice(0, limit);
|
|
75418
|
-
}
|
|
75419
|
-
function compareProfilesForLimit(a, b, currentProfile) {
|
|
75420
|
-
if (currentProfile) {
|
|
75421
|
-
if (a.name === currentProfile && b.name !== currentProfile) return -1;
|
|
75422
|
-
if (b.name === currentProfile && a.name !== currentProfile) return 1;
|
|
75423
|
-
}
|
|
75424
|
-
if (Boolean(a.isValid) !== Boolean(b.isValid)) return a.isValid ? -1 : 1;
|
|
75425
|
-
const aTimestamp = parseTimestamp(a.createdAt ?? null);
|
|
75426
|
-
const bTimestamp = parseTimestamp(b.createdAt ?? null);
|
|
75427
|
-
if (aTimestamp !== null && bTimestamp !== null) {
|
|
75428
|
-
if (aTimestamp === bTimestamp) return a.name.localeCompare(b.name);
|
|
75429
|
-
return bTimestamp - aTimestamp;
|
|
75430
|
-
}
|
|
75431
|
-
if (aTimestamp !== null) return -1;
|
|
75432
|
-
if (bTimestamp !== null) return 1;
|
|
75433
|
-
return a.name.localeCompare(b.name);
|
|
75434
|
-
}
|
|
75435
|
-
function parseTimestamp(value) {
|
|
75436
|
-
if (typeof value !== "string" || value.trim() === "") return null;
|
|
75437
|
-
const parsed = Date.parse(value);
|
|
75438
|
-
return Number.isNaN(parsed) ? null : parsed;
|
|
75439
|
-
}
|
|
75440
75892
|
//#endregion
|
|
75441
75893
|
//#region src/account-pool/service.ts
|
|
75442
75894
|
const ACCOUNT_POOL_DOCUMENT = "desktop.account-pool.v2";
|
|
@@ -75607,7 +76059,7 @@ function sessionTtlMs(affinityKind) {
|
|
|
75607
76059
|
}
|
|
75608
76060
|
}
|
|
75609
76061
|
function toStoreDbPath(stateDir) {
|
|
75610
|
-
return
|
|
76062
|
+
return nodePath.join(stateDir, "state.sqlite");
|
|
75611
76063
|
}
|
|
75612
76064
|
function createDefaultStore() {
|
|
75613
76065
|
return {
|
|
@@ -77479,15 +77931,19 @@ var LocalAccountPool = class LocalAccountPool {
|
|
|
77479
77931
|
return map;
|
|
77480
77932
|
}
|
|
77481
77933
|
async resolveProfiles(settings, store) {
|
|
77482
|
-
const [profilesView, autoRollSettings] = await Promise.all([loadProfilesViewData({
|
|
77934
|
+
const [profilesView, autoRollSettings] = await Promise.all([loadProfilesViewData({
|
|
77935
|
+
backgroundRefresh: false,
|
|
77936
|
+
autoImportActiveAuth: false
|
|
77937
|
+
}), getStoredAutoRollSettings()]);
|
|
77483
77938
|
const selectedProfileNames = settings.accountPoolProfilePool.length > 0 ? settings.accountPoolProfilePool.slice() : profilesView.profiles.map((profile) => profile.name);
|
|
77484
77939
|
const selectedSet = new Set(selectedProfileNames);
|
|
77485
|
-
const
|
|
77940
|
+
const switchRemainingThreshold = autoRollSettings?.switchRemainingThreshold ?? 5;
|
|
77486
77941
|
const now = Date.now();
|
|
77487
77942
|
const activeSessionCounts = this.buildActiveSessionCountMap(store);
|
|
77488
77943
|
const profileViews = profilesView.profiles.map((profile) => {
|
|
77489
77944
|
const selected = selectedSet.has(profile.name);
|
|
77490
77945
|
const usedPercent = maxRateLimitUsedPercent(profile);
|
|
77946
|
+
const remainingPercent = typeof usedPercent === "number" ? Math.max(0, 100 - usedPercent) : null;
|
|
77491
77947
|
const profileState = store.profilesByName[profile.name];
|
|
77492
77948
|
const cooldownUntilMs = profileState?.cooldownUntil ? Date.parse(profileState.cooldownUntil) : NaN;
|
|
77493
77949
|
let reason = null;
|
|
@@ -77495,7 +77951,7 @@ var LocalAccountPool = class LocalAccountPool {
|
|
|
77495
77951
|
else if (!profile.isValid) reason = "Profile auth is invalid.";
|
|
77496
77952
|
else if (profile.tokenStatus?.requiresUserAction) reason = profile.tokenStatus.reason ?? "Profile requires re-authentication.";
|
|
77497
77953
|
else if (Number.isFinite(cooldownUntilMs) && cooldownUntilMs > now) reason = `Cooling down until ${new Date(cooldownUntilMs).toLocaleTimeString()}.`;
|
|
77498
|
-
else if (
|
|
77954
|
+
else if (remainingPercent !== null && remainingPercent <= switchRemainingThreshold) reason = `Rate limit remaining is ${Math.round(remainingPercent)}%.`;
|
|
77499
77955
|
return {
|
|
77500
77956
|
name: profile.name,
|
|
77501
77957
|
displayName: profile.displayName ?? null,
|
|
@@ -77966,14 +78422,14 @@ function extractIconHref(source) {
|
|
|
77966
78422
|
}
|
|
77967
78423
|
function resolveIconHref(projectCwd, href) {
|
|
77968
78424
|
const clean = href.replace(/^\//, "");
|
|
77969
|
-
return [
|
|
78425
|
+
return [nodePath.join(projectCwd, "public", clean), nodePath.join(projectCwd, clean)];
|
|
77970
78426
|
}
|
|
77971
78427
|
function isPathWithinProject(projectCwd, candidatePath) {
|
|
77972
|
-
const relative =
|
|
77973
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
78428
|
+
const relative = nodePath.relative(nodePath.resolve(projectCwd), nodePath.resolve(candidatePath));
|
|
78429
|
+
return relative === "" || !relative.startsWith("..") && !nodePath.isAbsolute(relative);
|
|
77974
78430
|
}
|
|
77975
78431
|
function serveFaviconFile(filePath, res) {
|
|
77976
|
-
const contentType = FAVICON_MIME_TYPES[
|
|
78432
|
+
const contentType = FAVICON_MIME_TYPES[nodePath.extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
77977
78433
|
fs.readFile(filePath, (readErr, data) => {
|
|
77978
78434
|
if (readErr) {
|
|
77979
78435
|
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
@@ -78025,7 +78481,7 @@ function tryHandleProjectFaviconRequest(url, res) {
|
|
|
78025
78481
|
serveFallbackFavicon(res);
|
|
78026
78482
|
return;
|
|
78027
78483
|
}
|
|
78028
|
-
const sourceFile =
|
|
78484
|
+
const sourceFile = nodePath.join(projectCwd, ICON_SOURCE_FILES[index]);
|
|
78029
78485
|
fs.readFile(sourceFile, "utf8", (err, content) => {
|
|
78030
78486
|
if (err) {
|
|
78031
78487
|
trySourceFiles(index + 1);
|
|
@@ -78044,7 +78500,7 @@ function tryHandleProjectFaviconRequest(url, res) {
|
|
|
78044
78500
|
trySourceFiles(0);
|
|
78045
78501
|
return;
|
|
78046
78502
|
}
|
|
78047
|
-
const candidate =
|
|
78503
|
+
const candidate = nodePath.join(projectCwd, FAVICON_CANDIDATES[index]);
|
|
78048
78504
|
if (!isPathWithinProject(projectCwd, candidate)) {
|
|
78049
78505
|
tryCandidates(index + 1);
|
|
78050
78506
|
return;
|
|
@@ -78149,9 +78605,25 @@ const handleOrchestrationRequest = fnUntraced(function* (body, context) {
|
|
|
78149
78605
|
case ORCHESTRATION_WS_METHODS.getThreadSnapshot:
|
|
78150
78606
|
yield* context.threadSnapshotPreparation.prepareThreadSnapshot(body.threadId);
|
|
78151
78607
|
return yield* context.projectionReadModelQuery.getThreadSnapshot(stripRequestTag(body));
|
|
78152
|
-
case ORCHESTRATION_WS_METHODS.syncExternalThreads:
|
|
78153
|
-
|
|
78154
|
-
|
|
78608
|
+
case ORCHESTRATION_WS_METHODS.syncExternalThreads: {
|
|
78609
|
+
const input = stripRequestTag(body);
|
|
78610
|
+
if (input.projectId !== void 0) {
|
|
78611
|
+
const stats = yield* context.runExternalCodexThreadSyncForProject(input.projectId);
|
|
78612
|
+
return {
|
|
78613
|
+
syncedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
78614
|
+
completed: true,
|
|
78615
|
+
queued: false,
|
|
78616
|
+
stats
|
|
78617
|
+
};
|
|
78618
|
+
}
|
|
78619
|
+
const queued = yield* tryPromise(() => context.requestExternalCodexThreadSyncRefresh()).pipe(orElseSucceed(() => false));
|
|
78620
|
+
if (!queued) yield* forkDetach(context.runExternalCodexThreadSync.pipe(catch_((cause) => logWarning$1("failed to sync external Codex threads", { cause }))));
|
|
78621
|
+
return {
|
|
78622
|
+
syncedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
78623
|
+
completed: false,
|
|
78624
|
+
queued
|
|
78625
|
+
};
|
|
78626
|
+
}
|
|
78155
78627
|
case ORCHESTRATION_WS_METHODS.dispatchCommand: {
|
|
78156
78628
|
const normalizedCommand = yield* context.normalizeDispatchCommand({ command: body.command });
|
|
78157
78629
|
const overrideIntent = yield* context.resolveExternalThreadOverrideIntent(normalizedCommand);
|
|
@@ -78195,18 +78667,18 @@ const REGISTRY_TIMEOUT_MS = 1e4;
|
|
|
78195
78667
|
let freshnessCache = null;
|
|
78196
78668
|
function buildEnv() {
|
|
78197
78669
|
const env = { ...process.env };
|
|
78198
|
-
const homeDir = env.HOME ?? env.USERPROFILE ??
|
|
78670
|
+
const homeDir = env.HOME ?? env.USERPROFILE ?? nodeOs.homedir();
|
|
78199
78671
|
const extra = [
|
|
78200
78672
|
"/opt/homebrew/bin",
|
|
78201
78673
|
"/usr/local/bin",
|
|
78202
|
-
|
|
78203
|
-
|
|
78204
|
-
|
|
78205
|
-
|
|
78206
|
-
|
|
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")
|
|
78207
78679
|
];
|
|
78208
|
-
const segments = [...(env.PATH ?? "").split(
|
|
78209
|
-
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);
|
|
78210
78682
|
return env;
|
|
78211
78683
|
}
|
|
78212
78684
|
function runCommand$2(command, args, env) {
|
|
@@ -78218,7 +78690,7 @@ function runCommand$2(command, args, env) {
|
|
|
78218
78690
|
function resolveRunnableCommand(command, env, shellInfo) {
|
|
78219
78691
|
const resolved = resolveCommandPath(command, env, shellInfo)?.trim();
|
|
78220
78692
|
if (!resolved) return command;
|
|
78221
|
-
if (resolved.includes(
|
|
78693
|
+
if (resolved.includes(nodePath.sep) || resolved.includes("/")) return resolved;
|
|
78222
78694
|
return command;
|
|
78223
78695
|
}
|
|
78224
78696
|
function resolveShell() {
|
|
@@ -78548,15 +79020,15 @@ function isMissingPathError(error) {
|
|
|
78548
79020
|
return error.code === "ENOENT";
|
|
78549
79021
|
}
|
|
78550
79022
|
function resolveHomeDir() {
|
|
78551
|
-
const home = process.env.HOME || process.env.USERPROFILE ||
|
|
79023
|
+
const home = process.env.HOME || process.env.USERPROFILE || nodeOs.homedir();
|
|
78552
79024
|
if (!home) throw new Error("HOME is not set.");
|
|
78553
79025
|
return home;
|
|
78554
79026
|
}
|
|
78555
79027
|
function resolveCodexDir() {
|
|
78556
|
-
return
|
|
79028
|
+
return nodePath.join(resolveHomeDir(), ".codex");
|
|
78557
79029
|
}
|
|
78558
79030
|
function resolveLegacyPath(...segments) {
|
|
78559
|
-
return
|
|
79031
|
+
return nodePath.join(resolveCodexDir(), ...segments);
|
|
78560
79032
|
}
|
|
78561
79033
|
function pickAutoRoll(raw) {
|
|
78562
79034
|
if (!raw) return null;
|
|
@@ -78625,9 +79097,13 @@ function mergeLegacyLocalStoragePatch(payload) {
|
|
|
78625
79097
|
if (autoRoll) {
|
|
78626
79098
|
patch.autoRoll = {
|
|
78627
79099
|
enabled: autoRoll.enabled,
|
|
78628
|
-
|
|
78629
|
-
|
|
78630
|
-
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
|
|
78631
79107
|
};
|
|
78632
79108
|
markSkippedIfPresent("codex:auto-roll-settings", true);
|
|
78633
79109
|
} else markSkippedIfPresent("codex:auto-roll-settings", false);
|
|
@@ -78646,8 +79122,8 @@ async function removeIfExists(target) {
|
|
|
78646
79122
|
async function cleanupLegacyCanonicalSources() {
|
|
78647
79123
|
const homeDir = resolveHomeDir();
|
|
78648
79124
|
await removeIfExists(resolveLegacyAppStatePath());
|
|
78649
|
-
await removeIfExists(
|
|
78650
|
-
await removeIfExists(
|
|
79125
|
+
await removeIfExists(nodePath.join(getUserDataDir(), LEGACY_APP_SETTINGS_PARITY_FILE));
|
|
79126
|
+
await removeIfExists(nodePath.join(homeDir, SQLITE_STORAGE_DIR));
|
|
78651
79127
|
await removeIfExists(resolveLegacyPath(LEGACY_SETTINGS_FILE));
|
|
78652
79128
|
await removeIfExists(resolveLegacyPath(LEGACY_SETTINGS_BACKUP_FILE));
|
|
78653
79129
|
await removeIfExists(resolveLegacyPath(LEGACY_SYNC_STATE_FILE));
|
|
@@ -78658,7 +79134,7 @@ async function cleanupLegacyCanonicalSources() {
|
|
|
78658
79134
|
const profileHomes = await promises.readdir(profileHomesRoot, { withFileTypes: true });
|
|
78659
79135
|
for (const profileHome of profileHomes) {
|
|
78660
79136
|
if (!profileHome.isDirectory()) continue;
|
|
78661
|
-
const profileDir =
|
|
79137
|
+
const profileDir = nodePath.join(profileHomesRoot, profileHome.name);
|
|
78662
79138
|
let files = [];
|
|
78663
79139
|
try {
|
|
78664
79140
|
files = await promises.readdir(profileDir);
|
|
@@ -78667,7 +79143,7 @@ async function cleanupLegacyCanonicalSources() {
|
|
|
78667
79143
|
}
|
|
78668
79144
|
for (const file of files) {
|
|
78669
79145
|
if (!file.startsWith("profile.json")) continue;
|
|
78670
|
-
await removeIfExists(
|
|
79146
|
+
await removeIfExists(nodePath.join(profileDir, file));
|
|
78671
79147
|
}
|
|
78672
79148
|
}
|
|
78673
79149
|
} catch (error) {
|
|
@@ -78681,7 +79157,7 @@ async function cleanupLegacyCanonicalSources() {
|
|
|
78681
79157
|
const skillDirs = await promises.readdir(skillsRoot, { withFileTypes: true });
|
|
78682
79158
|
for (const entry of skillDirs) {
|
|
78683
79159
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
78684
|
-
await removeIfExists(
|
|
79160
|
+
await removeIfExists(nodePath.join(skillsRoot, entry.name, LEGACY_SKILL_MANIFEST));
|
|
78685
79161
|
}
|
|
78686
79162
|
} catch (error) {
|
|
78687
79163
|
if (!isMissingPathError(error)) logWarn("Failed cleaning legacy skills metadata:", {
|
|
@@ -78762,19 +79238,19 @@ async function importLegacyLocalStorageOnce(payload) {
|
|
|
78762
79238
|
//#region ../../packages/runtime-integrations/src/agents/service.ts
|
|
78763
79239
|
const AGENTS_FILENAME = "AGENTS.md";
|
|
78764
79240
|
function resolveGlobalPath(options) {
|
|
78765
|
-
return
|
|
79241
|
+
return nodePath.join(resolveCodexHomeDir(options), AGENTS_FILENAME);
|
|
78766
79242
|
}
|
|
78767
79243
|
function getCodexDir(options) {
|
|
78768
79244
|
return resolveCodexHomeDir(options);
|
|
78769
79245
|
}
|
|
78770
79246
|
function isPathSafe(normalizedPath, options) {
|
|
78771
|
-
if (
|
|
79247
|
+
if (nodePath.basename(normalizedPath) !== AGENTS_FILENAME) return false;
|
|
78772
79248
|
const codexDir = getCodexDir(options);
|
|
78773
79249
|
const homeDir = resolveHomeDir$1();
|
|
78774
|
-
if (normalizedPath ===
|
|
78775
|
-
const parentDir =
|
|
78776
|
-
if (!parentDir.startsWith(homeDir +
|
|
78777
|
-
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;
|
|
78778
79254
|
return true;
|
|
78779
79255
|
}
|
|
78780
79256
|
async function statFile(target) {
|
|
@@ -78797,7 +79273,7 @@ async function readFileContent(target) {
|
|
|
78797
79273
|
}
|
|
78798
79274
|
}
|
|
78799
79275
|
async function ensureDirForFile(target) {
|
|
78800
|
-
const dir =
|
|
79276
|
+
const dir = nodePath.dirname(target);
|
|
78801
79277
|
await promises.mkdir(dir, { recursive: true });
|
|
78802
79278
|
}
|
|
78803
79279
|
async function saveFileContent(target, content) {
|
|
@@ -78812,7 +79288,7 @@ function normalizeProjectPath(candidate) {
|
|
|
78812
79288
|
if (!candidate || typeof candidate !== "string") return null;
|
|
78813
79289
|
const trimmed = candidate.trim();
|
|
78814
79290
|
if (!trimmed) return null;
|
|
78815
|
-
return
|
|
79291
|
+
return nodePath.resolve(trimmed);
|
|
78816
79292
|
}
|
|
78817
79293
|
async function buildDescriptor(params) {
|
|
78818
79294
|
const stats = await statFile(params.path);
|
|
@@ -78838,7 +79314,7 @@ async function readPreferredContent(entries) {
|
|
|
78838
79314
|
async function getAgentsSnapshot(projectPath, options) {
|
|
78839
79315
|
const normalizedProject = normalizeProjectPath(projectPath);
|
|
78840
79316
|
const globalPath = resolveGlobalPath(options);
|
|
78841
|
-
const projectPathFile = normalizedProject ?
|
|
79317
|
+
const projectPathFile = normalizedProject ? nodePath.join(normalizedProject, AGENTS_FILENAME) : null;
|
|
78842
79318
|
const globalEntry = await buildDescriptor({
|
|
78843
79319
|
path: globalPath,
|
|
78844
79320
|
type: "global",
|
|
@@ -78860,7 +79336,7 @@ async function getAgentsSnapshot(projectPath, options) {
|
|
|
78860
79336
|
};
|
|
78861
79337
|
}
|
|
78862
79338
|
async function readAgentsFile(target, options) {
|
|
78863
|
-
const normalized =
|
|
79339
|
+
const normalized = nodePath.resolve(target);
|
|
78864
79340
|
if (!isPathSafe(normalized, options)) throw new Error("Access denied: path outside allowed directories");
|
|
78865
79341
|
const content = await readFileContent(normalized);
|
|
78866
79342
|
return {
|
|
@@ -78870,7 +79346,7 @@ async function readAgentsFile(target, options) {
|
|
|
78870
79346
|
};
|
|
78871
79347
|
}
|
|
78872
79348
|
async function saveAgentsFile(target, content, options) {
|
|
78873
|
-
const normalized =
|
|
79349
|
+
const normalized = nodePath.resolve(target);
|
|
78874
79350
|
if (!isPathSafe(normalized, options)) throw new Error("Access denied: path outside allowed directories");
|
|
78875
79351
|
await saveFileContent(normalized, content ?? "");
|
|
78876
79352
|
return readAgentsFile(normalized, options);
|
|
@@ -79070,7 +79546,7 @@ function formatMegabytes(bytes) {
|
|
|
79070
79546
|
return (bytes / MB_DIVISOR).toFixed(2);
|
|
79071
79547
|
}
|
|
79072
79548
|
function resolveSyncBackupsDir() {
|
|
79073
|
-
return
|
|
79549
|
+
return nodePath.join(getUserDataDir(), SYNC_BACKUP_DIR);
|
|
79074
79550
|
}
|
|
79075
79551
|
function toSafeIsoForFileName(iso) {
|
|
79076
79552
|
return iso.replace(/:/g, "-");
|
|
@@ -79078,14 +79554,14 @@ function toSafeIsoForFileName(iso) {
|
|
|
79078
79554
|
async function pruneOldPrePullBackups(backupsDir) {
|
|
79079
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));
|
|
79080
79556
|
if (files.length <= PRE_PULL_BACKUP_KEEP_COUNT) return;
|
|
79081
|
-
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 });
|
|
79082
79558
|
}
|
|
79083
79559
|
async function createPrePullBackup(profileManager) {
|
|
79084
79560
|
const snapshot = await buildLocalSnapshot(profileManager, { enforcePushGuards: false });
|
|
79085
79561
|
const backupsDir = resolveSyncBackupsDir();
|
|
79086
79562
|
await promises.mkdir(backupsDir, { recursive: true });
|
|
79087
79563
|
const timestamp = toSafeIsoForFileName((/* @__PURE__ */ new Date()).toISOString());
|
|
79088
|
-
const backupPath =
|
|
79564
|
+
const backupPath = nodePath.join(backupsDir, `${PRE_PULL_BACKUP_PREFIX}${timestamp}${PRE_PULL_BACKUP_SUFFIX}`);
|
|
79089
79565
|
await promises.writeFile(backupPath, `${JSON.stringify(snapshot, null, 2)}\n`, "utf8");
|
|
79090
79566
|
await pruneOldPrePullBackups(backupsDir);
|
|
79091
79567
|
logInfo("[cloud-sync] created pre-pull backup", { backupPath });
|
|
@@ -79319,17 +79795,17 @@ function buildRuntimeEnv(codexHomePath) {
|
|
|
79319
79795
|
...process.env,
|
|
79320
79796
|
...codexHomePath?.trim() ? { CODEX_HOME: codexHomePath.trim() } : {}
|
|
79321
79797
|
};
|
|
79322
|
-
const homeDir = env.HOME ?? env.USERPROFILE ??
|
|
79798
|
+
const homeDir = env.HOME ?? env.USERPROFILE ?? nodeOs.homedir();
|
|
79323
79799
|
const extraPaths = [
|
|
79324
79800
|
"/opt/homebrew/bin",
|
|
79325
79801
|
"/usr/local/bin",
|
|
79326
|
-
|
|
79327
|
-
|
|
79328
|
-
|
|
79329
|
-
|
|
79330
|
-
|
|
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")
|
|
79331
79807
|
];
|
|
79332
|
-
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);
|
|
79333
79809
|
return env;
|
|
79334
79810
|
}
|
|
79335
79811
|
function createTimeoutError(command, args) {
|
|
@@ -79723,6 +80199,7 @@ const AUTO_ROLL_ATTEMPT_TTL_MS = 3e4;
|
|
|
79723
80199
|
const EMPTY_USAGE_SUMMARY = {
|
|
79724
80200
|
windowKey: null,
|
|
79725
80201
|
usagePercent: 0,
|
|
80202
|
+
remainingPercent: 100,
|
|
79726
80203
|
hasUsage: false,
|
|
79727
80204
|
resetsInSeconds: null,
|
|
79728
80205
|
windowMinutes: null
|
|
@@ -79755,12 +80232,14 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
79755
80232
|
const rawUsedPercent = finiteNumberOrNull(window.usedPercent);
|
|
79756
80233
|
const hasUsage = rawUsedPercent !== null;
|
|
79757
80234
|
const usagePercent = hasUsage ? Math.max(0, Math.min(100, rawUsedPercent)) : 0;
|
|
80235
|
+
const remainingPercent = 100 - usagePercent;
|
|
79758
80236
|
const resetsInSeconds = resolveResetsInSeconds(window);
|
|
79759
80237
|
const windowMinutes = finiteNumberOrNull(window.windowMinutes);
|
|
79760
80238
|
if (!summary.windowKey) {
|
|
79761
80239
|
summary = {
|
|
79762
80240
|
windowKey: entry.key,
|
|
79763
80241
|
usagePercent,
|
|
80242
|
+
remainingPercent,
|
|
79764
80243
|
hasUsage,
|
|
79765
80244
|
resetsInSeconds,
|
|
79766
80245
|
windowMinutes
|
|
@@ -79771,6 +80250,7 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
79771
80250
|
summary = {
|
|
79772
80251
|
windowKey: entry.key,
|
|
79773
80252
|
usagePercent,
|
|
80253
|
+
remainingPercent,
|
|
79774
80254
|
hasUsage,
|
|
79775
80255
|
resetsInSeconds,
|
|
79776
80256
|
windowMinutes
|
|
@@ -79781,6 +80261,7 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
79781
80261
|
summary = {
|
|
79782
80262
|
windowKey: entry.key,
|
|
79783
80263
|
usagePercent,
|
|
80264
|
+
remainingPercent,
|
|
79784
80265
|
hasUsage,
|
|
79785
80266
|
resetsInSeconds,
|
|
79786
80267
|
windowMinutes
|
|
@@ -79791,6 +80272,7 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
79791
80272
|
if (resetsInSeconds !== null && resetsInSeconds < summary.resetsInSeconds) summary = {
|
|
79792
80273
|
windowKey: entry.key,
|
|
79793
80274
|
usagePercent,
|
|
80275
|
+
remainingPercent,
|
|
79794
80276
|
hasUsage,
|
|
79795
80277
|
resetsInSeconds,
|
|
79796
80278
|
windowMinutes
|
|
@@ -79800,6 +80282,7 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
79800
80282
|
if (!hasUsage && !summary.hasUsage && summary.resetsInSeconds === null && resetsInSeconds !== null) summary = {
|
|
79801
80283
|
windowKey: entry.key,
|
|
79802
80284
|
usagePercent,
|
|
80285
|
+
remainingPercent,
|
|
79803
80286
|
hasUsage,
|
|
79804
80287
|
resetsInSeconds,
|
|
79805
80288
|
windowMinutes
|
|
@@ -79807,26 +80290,42 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
79807
80290
|
}
|
|
79808
80291
|
return summary;
|
|
79809
80292
|
}
|
|
79810
|
-
function
|
|
79811
|
-
|
|
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;
|
|
79812
80311
|
const aReset = a.usageSummary.resetsInSeconds ?? Number.POSITIVE_INFINITY;
|
|
79813
80312
|
const bReset = b.usageSummary.resetsInSeconds ?? Number.POSITIVE_INFINITY;
|
|
79814
80313
|
if (aReset !== bReset) return aReset - bReset;
|
|
79815
80314
|
return a.profile.name.localeCompare(b.profile.name);
|
|
79816
80315
|
}
|
|
79817
|
-
function computeAutoRollCandidate(profiles, usageSummaries, currentProfileName, currentSummary,
|
|
79818
|
-
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) => ({
|
|
79819
80319
|
profile,
|
|
79820
80320
|
usageSummary: usageSummaries.get(profile.name) ?? summarizeRateLimitSnapshot(profile.rateLimit ?? null)
|
|
79821
80321
|
}));
|
|
79822
80322
|
if (candidates.length === 0) return null;
|
|
79823
|
-
let selected = candidates.filter((candidate) => candidate.usageSummary.hasUsage && candidate.usageSummary.
|
|
79824
|
-
if (!selected) selected = candidates.filter((candidate) => candidate.usageSummary.hasUsage).sort(compareAutoRollCandidates)[0] ?? null;
|
|
79825
|
-
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;
|
|
79826
80325
|
if (!selected) return null;
|
|
79827
80326
|
if (selected.usageSummary.hasUsage && currentSummary.hasUsage) {
|
|
79828
|
-
if (selected.usageSummary.
|
|
79829
|
-
if (selected.usageSummary.
|
|
80327
|
+
if (selected.usageSummary.remainingPercent < currentSummary.remainingPercent) return null;
|
|
80328
|
+
if (selected.usageSummary.remainingPercent === currentSummary.remainingPercent) {
|
|
79830
80329
|
if ((selected.usageSummary.resetsInSeconds ?? Number.POSITIVE_INFINITY) >= (currentSummary.resetsInSeconds ?? Number.POSITIVE_INFINITY)) return null;
|
|
79831
80330
|
}
|
|
79832
80331
|
}
|
|
@@ -81549,12 +82048,12 @@ var TelegramBridge = class {
|
|
|
81549
82048
|
}
|
|
81550
82049
|
buildRuntimeLockPath(token) {
|
|
81551
82050
|
const tokenHash = createHash("sha1").update(token).digest("hex").slice(0, 16);
|
|
81552
|
-
return
|
|
82051
|
+
return nodePath.join(getUserDataDir(), `${TELEGRAM_BRIDGE_LOCK_FILE_PREFIX}-${tokenHash}.lock`);
|
|
81553
82052
|
}
|
|
81554
82053
|
async acquireRuntimeLock(token) {
|
|
81555
82054
|
if (this.runtimeLockPath) return;
|
|
81556
82055
|
const lockPath = this.buildRuntimeLockPath(token);
|
|
81557
|
-
await fs$1.mkdir(
|
|
82056
|
+
await fs$1.mkdir(nodePath.dirname(lockPath), { recursive: true });
|
|
81558
82057
|
const payload = JSON.stringify({
|
|
81559
82058
|
pid: process.pid,
|
|
81560
82059
|
parentPid: process.ppid > 0 ? process.ppid : null,
|
|
@@ -81774,13 +82273,26 @@ function createTelegramBridge(options) {
|
|
|
81774
82273
|
return new TelegramBridge(options);
|
|
81775
82274
|
}
|
|
81776
82275
|
//#endregion
|
|
81777
|
-
//#region src/codex/officialAppRestart.ts
|
|
82276
|
+
//#region ../../packages/runtime-codex/src/codex/officialAppRestart.ts
|
|
81778
82277
|
const RESTART_COOLDOWN_MS = 6e4;
|
|
81779
82278
|
const COMMAND_TIMEOUT_MS = 3e3;
|
|
81780
|
-
const
|
|
82279
|
+
const EXIT_WAIT_MS = 1e4;
|
|
82280
|
+
const LAUNCH_WAIT_MS = 15e3;
|
|
81781
82281
|
const POLL_INTERVAL_MS = 250;
|
|
82282
|
+
const APP_DISCOVERY_CACHE_TTL_MS = 6e4;
|
|
82283
|
+
const APP_DISCOVERY_MISS_CACHE_TTL_MS = 5e3;
|
|
81782
82284
|
let restartPromise = null;
|
|
81783
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
|
+
}
|
|
81784
82296
|
function runCommand(command, args, timeoutMs = COMMAND_TIMEOUT_MS) {
|
|
81785
82297
|
return new Promise((resolve) => {
|
|
81786
82298
|
const child = spawn(command, args, { stdio: [
|
|
@@ -81821,15 +82333,252 @@ function runCommand(command, args, timeoutMs = COMMAND_TIMEOUT_MS) {
|
|
|
81821
82333
|
});
|
|
81822
82334
|
});
|
|
81823
82335
|
}
|
|
81824
|
-
function
|
|
81825
|
-
|
|
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));
|
|
81826
82366
|
}
|
|
81827
|
-
|
|
81828
|
-
|
|
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
|
+
};
|
|
82575
|
+
}
|
|
82576
|
+
async function readBundleString(appPath, key) {
|
|
82577
|
+
const plistPath = nodePath.join(appPath, "Contents", "Info.plist");
|
|
81829
82578
|
if (!existsSync(plistPath)) return null;
|
|
81830
82579
|
const result = await runCommand("/usr/bin/plutil", [
|
|
81831
82580
|
"-extract",
|
|
81832
|
-
|
|
82581
|
+
key,
|
|
81833
82582
|
"raw",
|
|
81834
82583
|
"-o",
|
|
81835
82584
|
"-",
|
|
@@ -81840,11 +82589,13 @@ async function readBundleIdentifier(appPath) {
|
|
|
81840
82589
|
return bundleId.length > 0 ? bundleId : null;
|
|
81841
82590
|
}
|
|
81842
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;
|
|
81843
82595
|
const rawPaths = /* @__PURE__ */ new Set();
|
|
81844
|
-
const envPath = process.env.CODEXUSE_OFFICIAL_CODEX_APP_PATH?.trim();
|
|
81845
82596
|
if (envPath) rawPaths.add(envPath);
|
|
81846
82597
|
rawPaths.add("/Applications/Codex.app");
|
|
81847
|
-
rawPaths.add(
|
|
82598
|
+
rawPaths.add(nodePath.join(homedir(), "Applications", "Codex.app"));
|
|
81848
82599
|
const mdfind = await runCommand("/usr/bin/mdfind", ["kMDItemFSName == 'Codex.app'"], 1500);
|
|
81849
82600
|
if (mdfind.code === 0) for (const line of mdfind.stdout.split(/\r?\n/)) {
|
|
81850
82601
|
const trimmed = line.trim();
|
|
@@ -81852,85 +82603,716 @@ async function discoverCodexAppCandidates() {
|
|
|
81852
82603
|
}
|
|
81853
82604
|
const candidates = [];
|
|
81854
82605
|
for (const appPath of rawPaths) {
|
|
81855
|
-
if (!existsSync(appPath) ||
|
|
81856
|
-
const bundleId = await
|
|
82606
|
+
if (!existsSync(appPath) || nodePath.basename(appPath) !== "Codex.app") continue;
|
|
82607
|
+
const bundleId = await readBundleString(appPath, "CFBundleIdentifier");
|
|
81857
82608
|
if (!bundleId || bundleId.toLowerCase().includes("codexuse")) continue;
|
|
81858
82609
|
candidates.push({
|
|
81859
82610
|
appPath,
|
|
81860
|
-
bundleId
|
|
82611
|
+
bundleId,
|
|
82612
|
+
version: await readBundleString(appPath, "CFBundleShortVersionString"),
|
|
82613
|
+
executableName: await readBundleString(appPath, "CFBundleExecutable") ?? "Codex"
|
|
81861
82614
|
});
|
|
81862
82615
|
}
|
|
81863
|
-
|
|
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;
|
|
81864
82623
|
}
|
|
81865
82624
|
async function getRunningCodexPids(candidate) {
|
|
81866
82625
|
const result = await runCommand("/bin/ps", ["-axo", "pid=,args="], 2e3);
|
|
81867
82626
|
const pids = /* @__PURE__ */ new Set();
|
|
81868
|
-
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);
|
|
81869
82641
|
if (result.code === 0) for (const line of result.stdout.split(/\r?\n/)) {
|
|
81870
82642
|
const match = line.match(/^\s*(\d+)\s+(.+)$/);
|
|
81871
82643
|
if (!match) continue;
|
|
81872
82644
|
const pid = Number(match[1]);
|
|
81873
82645
|
const args = match[2] ?? "";
|
|
81874
|
-
if (Number.isInteger(pid) && args.includes(
|
|
82646
|
+
if (Number.isInteger(pid) && args.includes(executablePath)) pids.add(pid);
|
|
82647
|
+
}
|
|
82648
|
+
return Array.from(pids).sort((a, b) => a - b);
|
|
82649
|
+
}
|
|
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
|
+
};
|
|
81875
82665
|
}
|
|
81876
|
-
|
|
82666
|
+
const [runningPids, mainPids] = await Promise.all([getRunningCodexPids(fallback), getRunningCodexMainPids(fallback)]);
|
|
82667
|
+
return {
|
|
82668
|
+
candidate: fallback,
|
|
82669
|
+
runningPids,
|
|
82670
|
+
mainPids
|
|
82671
|
+
};
|
|
81877
82672
|
}
|
|
81878
|
-
async function waitForCodexExit(candidate) {
|
|
81879
|
-
const deadline = Date.now() +
|
|
82673
|
+
async function waitForCodexExit(candidate, timeoutMs) {
|
|
82674
|
+
const deadline = Date.now() + timeoutMs;
|
|
81880
82675
|
while (Date.now() < deadline) {
|
|
81881
82676
|
if ((await getRunningCodexPids(candidate)).length === 0) return true;
|
|
81882
82677
|
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
81883
82678
|
}
|
|
81884
82679
|
return false;
|
|
81885
82680
|
}
|
|
81886
|
-
async function
|
|
81887
|
-
|
|
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;
|
|
81888
82688
|
}
|
|
81889
82689
|
async function openCodex(candidate) {
|
|
81890
82690
|
if ((await runCommand("/usr/bin/open", ["-b", candidate.bundleId])).code === 0) return true;
|
|
81891
82691
|
return (await runCommand("/usr/bin/open", [candidate.appPath])).code === 0;
|
|
81892
82692
|
}
|
|
81893
|
-
async function
|
|
81894
|
-
|
|
81895
|
-
|
|
81896
|
-
|
|
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
|
|
81897
82867
|
};
|
|
81898
|
-
|
|
81899
|
-
|
|
81900
|
-
|
|
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
|
|
81901
82877
|
};
|
|
81902
|
-
|
|
81903
|
-
|
|
81904
|
-
|
|
81905
|
-
reason: "official-codex-app-not-found"
|
|
82878
|
+
if (process.platform !== "darwin") return {
|
|
82879
|
+
...base,
|
|
82880
|
+
state: "unsupported"
|
|
81906
82881
|
};
|
|
81907
|
-
|
|
81908
|
-
|
|
81909
|
-
|
|
82882
|
+
const { candidate, mainPids } = await resolveCodexAppTarget();
|
|
82883
|
+
if (!candidate) return {
|
|
82884
|
+
...base,
|
|
82885
|
+
state: "not-found"
|
|
81910
82886
|
};
|
|
81911
|
-
|
|
81912
|
-
|
|
81913
|
-
|
|
81914
|
-
status: "failed",
|
|
82887
|
+
if (mainPids.length === 0) return {
|
|
82888
|
+
...base,
|
|
82889
|
+
state: "not-running",
|
|
81915
82890
|
appPath: candidate.appPath,
|
|
81916
82891
|
bundleId: candidate.bundleId,
|
|
81917
|
-
|
|
82892
|
+
runningPids: mainPids
|
|
81918
82893
|
};
|
|
81919
|
-
|
|
81920
|
-
|
|
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",
|
|
81921
82901
|
appPath: candidate.appPath,
|
|
81922
82902
|
bundleId: candidate.bundleId,
|
|
81923
|
-
|
|
82903
|
+
runningPids: mainPids
|
|
81924
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
|
+
}
|
|
81925
82951
|
return {
|
|
81926
|
-
|
|
82952
|
+
state: "found",
|
|
81927
82953
|
appPath: candidate.appPath,
|
|
81928
|
-
bundleId: candidate.bundleId
|
|
82954
|
+
bundleId: candidate.bundleId,
|
|
82955
|
+
version: candidate.version,
|
|
82956
|
+
instances: managed,
|
|
82957
|
+
unmanaged
|
|
81929
82958
|
};
|
|
81930
82959
|
}
|
|
81931
|
-
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 = {}) {
|
|
81932
83310
|
if (restartPromise) return restartPromise;
|
|
81933
|
-
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(() => {
|
|
81934
83316
|
restartPromise = null;
|
|
81935
83317
|
});
|
|
81936
83318
|
return restartPromise;
|
|
@@ -82041,6 +83423,37 @@ function describeActiveAuthOwner(snapshot) {
|
|
|
82041
83423
|
if (!snapshot) return null;
|
|
82042
83424
|
return snapshot.email ?? snapshot.userId ?? snapshot.chatgptUserId ?? snapshot.accountId ?? null;
|
|
82043
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
|
+
}
|
|
82044
83457
|
async function resolveCodexBinaryOrThrow(context) {
|
|
82045
83458
|
try {
|
|
82046
83459
|
return await requireCodexCli();
|
|
@@ -82048,7 +83461,7 @@ async function resolveCodexBinaryOrThrow(context) {
|
|
|
82048
83461
|
if (error instanceof CodexCliMissingError) {
|
|
82049
83462
|
const reason = error.status.reason ?? "Codex CLI not found. Install @openai/codex or set CODEX_BINARY.";
|
|
82050
83463
|
const message = context ? `${context} ${reason}` : reason;
|
|
82051
|
-
throw new Error(message);
|
|
83464
|
+
throw new Error(message, { cause: error });
|
|
82052
83465
|
}
|
|
82053
83466
|
throw error;
|
|
82054
83467
|
}
|
|
@@ -82144,6 +83557,8 @@ const createServer = fn(function* () {
|
|
|
82144
83557
|
clients,
|
|
82145
83558
|
logOutgoingPush
|
|
82146
83559
|
});
|
|
83560
|
+
setExternalCodexThreadSyncProgressReporter((payload) => runPromise(pushBus.publishAll(WS_CHANNELS.externalThreadSyncProgress, payload)));
|
|
83561
|
+
yield* addFinalizer(() => sync(() => setExternalCodexThreadSyncProgressReporter(null)));
|
|
82147
83562
|
const agentChatConfig = getAgentChatConfig();
|
|
82148
83563
|
const generalChatConfig = getGeneralChatConfig();
|
|
82149
83564
|
yield* readiness.markPushBusReady;
|
|
@@ -82753,6 +84168,61 @@ const createServer = fn(function* () {
|
|
|
82753
84168
|
properties
|
|
82754
84169
|
});
|
|
82755
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
|
+
};
|
|
82756
84226
|
const publishProfileSwitched = (payload) => {
|
|
82757
84227
|
refreshTray();
|
|
82758
84228
|
return runPromise$2(pushBus.publishAll(WS_CHANNELS.profilesSwitched, payload));
|
|
@@ -82760,6 +84230,7 @@ const createServer = fn(function* () {
|
|
|
82760
84230
|
const publishCliStatusUpdated = (payload) => runPromise$2(pushBus.publishAll(WS_CHANNELS.cliStatusUpdated, payload));
|
|
82761
84231
|
let backgroundAutoRollInFlight = false;
|
|
82762
84232
|
let lastAutoRollAttempt = null;
|
|
84233
|
+
const autoRollRearmBlockedProfiles = /* @__PURE__ */ new Set();
|
|
82763
84234
|
const rememberAutoRollAttempt = (source, target) => {
|
|
82764
84235
|
if (!source || !target) return;
|
|
82765
84236
|
lastAutoRollAttempt = {
|
|
@@ -82772,29 +84243,177 @@ const createServer = fn(function* () {
|
|
|
82772
84243
|
if (!lastAutoRollAttempt) return false;
|
|
82773
84244
|
return lastAutoRollAttempt.source === source && lastAutoRollAttempt.target === target && Date.now() - lastAutoRollAttempt.timestamp < 3e4;
|
|
82774
84245
|
};
|
|
82775
|
-
const
|
|
82776
|
-
|
|
82777
|
-
|
|
82778
|
-
|
|
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
|
+
});
|
|
82779
84378
|
trackAnalyticsEvent("official_codex_restart_after_auto_roll", {
|
|
82780
84379
|
status: result.status,
|
|
82781
84380
|
reason: result.status === "skipped" || result.status === "failed" ? result.reason : void 0,
|
|
82782
84381
|
targetProfileName
|
|
82783
84382
|
});
|
|
82784
84383
|
if (result.status === "failed") logger.warn("Official Codex restart after auto-roll failed", result);
|
|
82785
|
-
else if (result.status === "restarted") logger.info("Official Codex restarted after auto-roll", result);
|
|
82786
|
-
}
|
|
84384
|
+
else if (result.status === "restarted" || result.status === "started") logger.info("Official Codex restarted after auto-roll", result);
|
|
84385
|
+
} catch (error) {
|
|
82787
84386
|
logger.warn("Official Codex restart after auto-roll threw", { error });
|
|
82788
|
-
}
|
|
84387
|
+
}
|
|
82789
84388
|
};
|
|
82790
84389
|
const handleProfileSwitch = async (name, source = "app") => {
|
|
82791
84390
|
if (!name) throw new Error("Profile name is required");
|
|
82792
84391
|
const previousProfile = source === "auto-roll" ? (await profileManager.getCurrentProfile()).name ?? null : null;
|
|
82793
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
|
+
}
|
|
82794
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
|
+
}
|
|
82795
84414
|
await publishProfileSwitched({ name });
|
|
82796
84415
|
trackAnalyticsEvent("profile_switched", { source });
|
|
82797
|
-
if (source === "auto-roll") maybeRestartOfficialCodexAfterAutoRoll(name);
|
|
84416
|
+
if (source === "auto-roll") await maybeRestartOfficialCodexAfterAutoRoll(name, targetProfileKey);
|
|
82798
84417
|
};
|
|
82799
84418
|
const maybeRunBackgroundAutoRoll = async () => {
|
|
82800
84419
|
if (backgroundAutoRollInFlight) return;
|
|
@@ -82818,14 +84437,17 @@ const createServer = fn(function* () {
|
|
|
82818
84437
|
rateLimit: snapshot
|
|
82819
84438
|
};
|
|
82820
84439
|
});
|
|
84440
|
+
rearmAutoRollBlockedProfiles(usageSummaries, settings.rearmRemainingThreshold);
|
|
82821
84441
|
const currentProfile = profilesWithSnapshots.find((profile) => profile.name === currentProfileName);
|
|
82822
84442
|
if (!currentProfile) return;
|
|
82823
84443
|
const currentSummary = usageSummaries.get(currentProfileName) ?? summarizeRateLimitSnapshot(currentProfile.rateLimit ?? null);
|
|
82824
|
-
if (!currentSummary.hasUsage
|
|
82825
|
-
|
|
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);
|
|
82826
84448
|
if (!candidate || isRecentAutoRollAttempt(currentProfileName, candidate.profile.name)) return;
|
|
82827
|
-
rememberAutoRollAttempt(currentProfileName, candidate.profile.name);
|
|
82828
84449
|
await handleProfileSwitch(candidate.profile.name, "auto-roll");
|
|
84450
|
+
blockAutoRollUntilRearm(currentProfileName);
|
|
82829
84451
|
} catch (error) {
|
|
82830
84452
|
logger.warn("Background auto-roll failed", { error });
|
|
82831
84453
|
} finally {
|
|
@@ -82858,6 +84480,9 @@ const createServer = fn(function* () {
|
|
|
82858
84480
|
profileName: payload.profileName,
|
|
82859
84481
|
snapshot: payload.snapshot ?? null
|
|
82860
84482
|
}));
|
|
84483
|
+
maybeSendLowRemainingNotification(payload).catch((error) => {
|
|
84484
|
+
logger.warn("Low remaining notification failed", { error });
|
|
84485
|
+
});
|
|
82861
84486
|
maybeRunBackgroundAutoRoll();
|
|
82862
84487
|
};
|
|
82863
84488
|
const handleRateLimitStateChanged = (payload) => {
|
|
@@ -82906,7 +84531,8 @@ const createServer = fn(function* () {
|
|
|
82906
84531
|
persistExternalThreadOverrideIntent,
|
|
82907
84532
|
dispatchOrchestrationCommand,
|
|
82908
84533
|
requestExternalCodexThreadSyncRefresh: () => requestExternalCodexThreadSyncRefresh("renderer-ws"),
|
|
82909
|
-
runExternalCodexThreadSync
|
|
84534
|
+
runExternalCodexThreadSync,
|
|
84535
|
+
runExternalCodexThreadSyncForProject
|
|
82910
84536
|
};
|
|
82911
84537
|
const routeRequest = fnUntraced(function* (request, ws) {
|
|
82912
84538
|
const orchestrationResult = yield* handleOrchestrationRequest(request.body, orchestrationRequestContext);
|
|
@@ -83265,9 +84891,15 @@ const createServer = fn(function* () {
|
|
|
83265
84891
|
return yield* promise(() => telegramBridge.testToken(body.token));
|
|
83266
84892
|
}
|
|
83267
84893
|
case WS_METHODS.profilesFetch: {
|
|
83268
|
-
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));
|
|
83269
84898
|
refreshTray();
|
|
83270
|
-
return
|
|
84899
|
+
return {
|
|
84900
|
+
...data,
|
|
84901
|
+
officialCodexStatus
|
|
84902
|
+
};
|
|
83271
84903
|
}
|
|
83272
84904
|
case WS_METHODS.profilesStartAuth: {
|
|
83273
84905
|
const body = stripRequestTag(request.body);
|
|
@@ -83279,30 +84911,23 @@ const createServer = fn(function* () {
|
|
|
83279
84911
|
if (yield* promise(() => profileManager.profileExists(name))) return yield* new RouteRequestError({ message: `Profile '${name}' already exists! Choose a different name.` });
|
|
83280
84912
|
} else if (!(yield* promise(() => profileManager.profileExists(name)))) return yield* new RouteRequestError({ message: `Profile '${name}' not found!` });
|
|
83281
84913
|
const existingSession = authSessions.get(name);
|
|
83282
|
-
if (existingSession
|
|
83283
|
-
existingSession.process.kill();
|
|
84914
|
+
if (existingSession) {
|
|
84915
|
+
if (!existingSession.completed) existingSession.process.kill();
|
|
83284
84916
|
if (existingSession.timeout) clearTimeout(existingSession.timeout);
|
|
83285
|
-
|
|
84917
|
+
cleanupProfileAuthSession(existingSession);
|
|
83286
84918
|
authSessions.delete(name);
|
|
83287
84919
|
}
|
|
83288
84920
|
const startedAt = Date.now();
|
|
83289
|
-
const
|
|
83290
|
-
|
|
83291
|
-
const restoreConfig = async () => {
|
|
83292
|
-
if (!cleanupConfig) return;
|
|
83293
|
-
const restore = cleanupConfig;
|
|
83294
|
-
cleanupConfig = null;
|
|
83295
|
-
try {
|
|
83296
|
-
await restore();
|
|
83297
|
-
} catch (error) {
|
|
83298
|
-
console.warn("Failed to restore Codex config after login attempt:", error);
|
|
83299
|
-
}
|
|
83300
|
-
};
|
|
84921
|
+
const loginHome = yield* promise(() => createIsolatedCodexLoginHome());
|
|
84922
|
+
const initialAuthSnapshot = yield* promise(() => profileManager.captureAuthSnapshot(loginHome.authPath));
|
|
83301
84923
|
try {
|
|
83302
|
-
const storageOptions = yield* promise(() => resolveCodexHomeStorageOptions());
|
|
83303
|
-
cleanupConfig = yield* promise(() => sanitizeCodexConfigForCli(storageOptions));
|
|
83304
84924
|
const codexPath = yield* promise(() => resolveCodexBinaryOrThrow("Codex CLI is required to start authentication."));
|
|
83305
|
-
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
|
+
});
|
|
83306
84931
|
const command = buildCodexCommand$1(codexPath, ["login"], loginEnv);
|
|
83307
84932
|
const loginProcess = spawn(command.command, command.args, {
|
|
83308
84933
|
stdio: [
|
|
@@ -83322,7 +84947,8 @@ const createServer = fn(function* () {
|
|
|
83322
84947
|
mode,
|
|
83323
84948
|
startedAt,
|
|
83324
84949
|
initialAuthSnapshot,
|
|
83325
|
-
|
|
84950
|
+
authPath: loginHome.authPath,
|
|
84951
|
+
cleanupAuthHome: loginHome.cleanup
|
|
83326
84952
|
};
|
|
83327
84953
|
authSessions.set(name, session);
|
|
83328
84954
|
loginProcess.stdout?.on("data", (chunk) => {
|
|
@@ -83342,7 +84968,6 @@ const createServer = fn(function* () {
|
|
|
83342
84968
|
session.completed = true;
|
|
83343
84969
|
session.exitCode = code;
|
|
83344
84970
|
if (session.timeout) clearTimeout(session.timeout);
|
|
83345
|
-
restoreConfig();
|
|
83346
84971
|
});
|
|
83347
84972
|
loginProcess.on("error", (error) => {
|
|
83348
84973
|
session.error = compactErrorText(error instanceof Error ? error.message : String(error)) || "Authentication failed.";
|
|
@@ -83352,7 +84977,6 @@ const createServer = fn(function* () {
|
|
|
83352
84977
|
clearTimeout(session.timeout);
|
|
83353
84978
|
session.timeout = void 0;
|
|
83354
84979
|
}
|
|
83355
|
-
restoreConfig();
|
|
83356
84980
|
});
|
|
83357
84981
|
session.timeout = setTimeout(() => {
|
|
83358
84982
|
if (!session.completed) {
|
|
@@ -83366,7 +84990,7 @@ const createServer = fn(function* () {
|
|
|
83366
84990
|
return;
|
|
83367
84991
|
} catch (error) {
|
|
83368
84992
|
trackAnalyticsEvent("profile_auth_start_failed", { mode });
|
|
83369
|
-
yield* promise(() =>
|
|
84993
|
+
yield* promise(() => loginHome.cleanup());
|
|
83370
84994
|
throw error;
|
|
83371
84995
|
}
|
|
83372
84996
|
}
|
|
@@ -83391,38 +85015,41 @@ const createServer = fn(function* () {
|
|
|
83391
85015
|
const session = authSessions.get(body.name);
|
|
83392
85016
|
if (!session) return yield* new RouteRequestError({ message: "No authentication session found" });
|
|
83393
85017
|
if (!session.completed) return yield* new RouteRequestError({ message: "Authentication not yet completed" });
|
|
83394
|
-
|
|
83395
|
-
|
|
83396
|
-
|
|
83397
|
-
|
|
83398
|
-
|
|
83399
|
-
|
|
83400
|
-
const currentAuthSnapshot = yield* promise(() => profileManager.captureActiveAuthSnapshot());
|
|
83401
|
-
if (currentAuthSnapshot.fingerprint === (session.initialAuthSnapshot?.fingerprint ?? null)) {
|
|
83402
|
-
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) {
|
|
83403
85024
|
trackAnalyticsEvent("profile_auth_complete_failed", { mode: session.mode });
|
|
83404
|
-
|
|
83405
|
-
|
|
83406
|
-
|
|
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
|
+
}
|
|
83407
85035
|
}
|
|
83408
|
-
}
|
|
83409
|
-
try {
|
|
83410
85036
|
if (session.mode === "refresh") {
|
|
83411
|
-
yield* promise(() => profileManager.
|
|
85037
|
+
yield* promise(() => profileManager.refreshProfileAuthFromFile(body.name, session.authPath));
|
|
83412
85038
|
trackAnalyticsEvent("profile_tokens_refreshed", { mode: session.mode });
|
|
83413
85039
|
} else {
|
|
83414
85040
|
yield* promise(() => assertProfileCreationAllowed(profileManager));
|
|
83415
|
-
yield* promise(() => profileManager.
|
|
85041
|
+
yield* promise(() => profileManager.createProfileFromAuthFile(body.name, session.authPath));
|
|
83416
85042
|
trackAnalyticsEvent("profile_created", { mode: session.mode });
|
|
83417
85043
|
}
|
|
83418
85044
|
trackAnalyticsEvent("profile_auth_completed", { mode: session.mode });
|
|
83419
|
-
authSessions.delete(body.name);
|
|
83420
85045
|
refreshTray();
|
|
83421
85046
|
return;
|
|
83422
85047
|
} catch (error) {
|
|
83423
85048
|
trackAnalyticsEvent("profile_auth_complete_failed", { mode: session.mode });
|
|
83424
|
-
authSessions.delete(body.name);
|
|
83425
85049
|
throw error;
|
|
85050
|
+
} finally {
|
|
85051
|
+
authSessions.delete(body.name);
|
|
85052
|
+
yield* promise(() => cleanupProfileAuthSession(session));
|
|
83426
85053
|
}
|
|
83427
85054
|
}
|
|
83428
85055
|
case WS_METHODS.profilesSwitch: {
|
|
@@ -83446,13 +85073,39 @@ const createServer = fn(function* () {
|
|
|
83446
85073
|
}
|
|
83447
85074
|
case WS_METHODS.profilesDelete: {
|
|
83448
85075
|
const body = stripRequestTag(request.body);
|
|
83449
|
-
|
|
83450
|
-
yield*
|
|
83451
|
-
|
|
83452
|
-
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" });
|
|
83453
85108
|
refreshTray();
|
|
83454
|
-
const currentProfileName = typeof currentProfile.name === "string" && currentProfile.name.length > 0 ? currentProfile.name : null;
|
|
83455
|
-
if (currentProfileName) yield* promise(() => publishProfileSwitched({ name: currentProfileName }));
|
|
83456
85109
|
return;
|
|
83457
85110
|
}
|
|
83458
85111
|
case WS_METHODS.profilesExport: {
|
|
@@ -83473,12 +85126,12 @@ const createServer = fn(function* () {
|
|
|
83473
85126
|
clearTimeout(session.timeout);
|
|
83474
85127
|
session.timeout = void 0;
|
|
83475
85128
|
}
|
|
83476
|
-
|
|
85129
|
+
yield* promise(() => cleanupProfileAuthSession(session));
|
|
83477
85130
|
authSessions.delete(body.name);
|
|
83478
85131
|
return;
|
|
83479
85132
|
}
|
|
83480
85133
|
case WS_METHODS.rateLimitsRefreshNow: {
|
|
83481
|
-
const profiles = yield* promise(() => profileManager
|
|
85134
|
+
const { profiles } = yield* promise(() => loadLicenseVisibleProfiles(profileManager));
|
|
83482
85135
|
const profileNames = Array.from(new Set(profiles.filter((profile) => profile.isValid).map((profile) => profile.name).filter(Boolean)));
|
|
83483
85136
|
return { enqueued: (yield* promise(() => enqueueAccountsRefreshNow(profileNames))).enqueuedAccountIds.length };
|
|
83484
85137
|
}
|
|
@@ -83517,15 +85170,197 @@ const createServer = fn(function* () {
|
|
|
83517
85170
|
case WS_METHODS.autoRollSaveSettings: {
|
|
83518
85171
|
const normalized = normalizeAutoRollSettings(stripRequestTag(request.body).settings);
|
|
83519
85172
|
yield* promise(() => persistAutoRollSettings(normalized));
|
|
85173
|
+
maybeRunBackgroundAutoRoll();
|
|
83520
85174
|
trackAnalyticsEvent("auto_roll_settings_saved", {
|
|
83521
85175
|
enabled: normalized.enabled,
|
|
83522
|
-
|
|
83523
|
-
|
|
83524
|
-
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
|
|
83525
85183
|
});
|
|
83526
85184
|
return normalized;
|
|
83527
85185
|
}
|
|
83528
|
-
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
|
+
}
|
|
83529
85364
|
case WS_METHODS.cliInfo: return yield* promise(() => getCodexCliInfo());
|
|
83530
85365
|
case WS_METHODS.cliStatus: return yield* promise(() => getCliInstallStatusWithFreshness());
|
|
83531
85366
|
case WS_METHODS.cliCheckFreshness: {
|
|
@@ -83605,8 +85440,8 @@ const ServerLive = succeed$3(Server, {
|
|
|
83605
85440
|
//#region src/serverLogger.ts
|
|
83606
85441
|
const ServerLoggerLive = gen(function* () {
|
|
83607
85442
|
const config = yield* ServerConfig$1;
|
|
83608
|
-
const logDir =
|
|
83609
|
-
const logPath =
|
|
85443
|
+
const logDir = nodePath.join(config.stateDir, "logs");
|
|
85444
|
+
const logPath = nodePath.join(logDir, "server.log");
|
|
83610
85445
|
yield* sync(() => {
|
|
83611
85446
|
fs.mkdirSync(logDir, { recursive: true });
|
|
83612
85447
|
});
|