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/index.js
CHANGED
|
@@ -2094,6 +2094,117 @@ function normalizeCommitMessagePrompt(value) {
|
|
|
2094
2094
|
return normalized.length > 0 ? normalized : null;
|
|
2095
2095
|
}
|
|
2096
2096
|
|
|
2097
|
+
// ../../packages/contracts/src/settings/auto-roll.ts
|
|
2098
|
+
var DEFAULT_AUTO_ROLL_ENABLED = false;
|
|
2099
|
+
var DEFAULT_AUTO_ROLL_REARM_REMAINING_THRESHOLD = 15;
|
|
2100
|
+
var DEFAULT_AUTO_ROLL_SWITCH_REMAINING_THRESHOLD = 5;
|
|
2101
|
+
var DEFAULT_RESTART_OFFICIAL_CODEX_ON_AUTO_ROLL = false;
|
|
2102
|
+
var DEFAULT_LAUNCH_OFFICIAL_CODEX_WHEN_CLOSED_ON_AUTO_ROLL = false;
|
|
2103
|
+
var DEFAULT_AUTO_ROLL_PRIORITY_ORDER = [];
|
|
2104
|
+
var DEFAULT_LOW_REMAINING_NOTIFICATION_ENABLED = false;
|
|
2105
|
+
var DEFAULT_LOW_REMAINING_NOTIFICATION_THRESHOLD = 1;
|
|
2106
|
+
var AUTO_ROLL_SWITCH_REMAINING_MIN = 0;
|
|
2107
|
+
var AUTO_ROLL_SWITCH_REMAINING_MAX = 50;
|
|
2108
|
+
var AUTO_ROLL_REARM_REMAINING_MAX = 100;
|
|
2109
|
+
var LOW_REMAINING_NOTIFICATION_MIN = 1;
|
|
2110
|
+
var LOW_REMAINING_NOTIFICATION_MAX = 50;
|
|
2111
|
+
function clampNumber(value, min, max) {
|
|
2112
|
+
return Math.min(max, Math.max(min, value));
|
|
2113
|
+
}
|
|
2114
|
+
function resolveFiniteNumber(value, fallback) {
|
|
2115
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
2116
|
+
}
|
|
2117
|
+
function legacyUsedThresholdToRemaining(value, fallbackUsed) {
|
|
2118
|
+
const used = clampNumber(resolveFiniteNumber(value, fallbackUsed), 0, 100);
|
|
2119
|
+
return 100 - used;
|
|
2120
|
+
}
|
|
2121
|
+
function sanitizeAutoRollSwitchRemainingThreshold(value) {
|
|
2122
|
+
const numeric = resolveFiniteNumber(
|
|
2123
|
+
value,
|
|
2124
|
+
DEFAULT_AUTO_ROLL_SWITCH_REMAINING_THRESHOLD
|
|
2125
|
+
);
|
|
2126
|
+
return clampNumber(
|
|
2127
|
+
numeric,
|
|
2128
|
+
AUTO_ROLL_SWITCH_REMAINING_MIN,
|
|
2129
|
+
AUTO_ROLL_SWITCH_REMAINING_MAX
|
|
2130
|
+
);
|
|
2131
|
+
}
|
|
2132
|
+
function sanitizeAutoRollRearmRemainingThreshold(value, switchRemainingThreshold) {
|
|
2133
|
+
const sanitizedSwitch = sanitizeAutoRollSwitchRemainingThreshold(
|
|
2134
|
+
switchRemainingThreshold
|
|
2135
|
+
);
|
|
2136
|
+
const numeric = resolveFiniteNumber(
|
|
2137
|
+
value,
|
|
2138
|
+
DEFAULT_AUTO_ROLL_REARM_REMAINING_THRESHOLD
|
|
2139
|
+
);
|
|
2140
|
+
return clampNumber(
|
|
2141
|
+
numeric,
|
|
2142
|
+
sanitizedSwitch + 1,
|
|
2143
|
+
AUTO_ROLL_REARM_REMAINING_MAX
|
|
2144
|
+
);
|
|
2145
|
+
}
|
|
2146
|
+
function sanitizeAutoRollThresholds(rearmRemainingThreshold, switchRemainingThreshold) {
|
|
2147
|
+
const sanitizedSwitch = sanitizeAutoRollSwitchRemainingThreshold(
|
|
2148
|
+
switchRemainingThreshold
|
|
2149
|
+
);
|
|
2150
|
+
const sanitizedRearm = sanitizeAutoRollRearmRemainingThreshold(
|
|
2151
|
+
rearmRemainingThreshold,
|
|
2152
|
+
sanitizedSwitch
|
|
2153
|
+
);
|
|
2154
|
+
return {
|
|
2155
|
+
rearmRemainingThreshold: sanitizedRearm,
|
|
2156
|
+
switchRemainingThreshold: sanitizedSwitch
|
|
2157
|
+
};
|
|
2158
|
+
}
|
|
2159
|
+
function sanitizeAutoRollPriorityOrder(value) {
|
|
2160
|
+
if (!Array.isArray(value)) {
|
|
2161
|
+
return [...DEFAULT_AUTO_ROLL_PRIORITY_ORDER];
|
|
2162
|
+
}
|
|
2163
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2164
|
+
const normalized = [];
|
|
2165
|
+
for (const item of value) {
|
|
2166
|
+
if (typeof item !== "string") {
|
|
2167
|
+
continue;
|
|
2168
|
+
}
|
|
2169
|
+
const trimmed = item.trim();
|
|
2170
|
+
if (!trimmed || seen.has(trimmed)) {
|
|
2171
|
+
continue;
|
|
2172
|
+
}
|
|
2173
|
+
seen.add(trimmed);
|
|
2174
|
+
normalized.push(trimmed);
|
|
2175
|
+
}
|
|
2176
|
+
return normalized;
|
|
2177
|
+
}
|
|
2178
|
+
function sanitizeLowRemainingNotificationThreshold(value) {
|
|
2179
|
+
const numeric = resolveFiniteNumber(value, DEFAULT_LOW_REMAINING_NOTIFICATION_THRESHOLD);
|
|
2180
|
+
return clampNumber(
|
|
2181
|
+
numeric,
|
|
2182
|
+
LOW_REMAINING_NOTIFICATION_MIN,
|
|
2183
|
+
LOW_REMAINING_NOTIFICATION_MAX
|
|
2184
|
+
);
|
|
2185
|
+
}
|
|
2186
|
+
function normalizeAutoRollSettings(raw) {
|
|
2187
|
+
const enabled = typeof raw?.enabled === "boolean" ? raw.enabled : DEFAULT_AUTO_ROLL_ENABLED;
|
|
2188
|
+
const rawSwitchRemaining = typeof raw?.switchRemainingThreshold === "number" ? raw.switchRemainingThreshold : legacyUsedThresholdToRemaining(raw?.switchThreshold, 95);
|
|
2189
|
+
const rawRearmRemaining = typeof raw?.rearmRemainingThreshold === "number" ? raw.rearmRemainingThreshold : legacyUsedThresholdToRemaining(raw?.warningThreshold, 85);
|
|
2190
|
+
const {
|
|
2191
|
+
rearmRemainingThreshold: normalizedRearm,
|
|
2192
|
+
switchRemainingThreshold: normalizedSwitch
|
|
2193
|
+
} = sanitizeAutoRollThresholds(rawRearmRemaining, rawSwitchRemaining);
|
|
2194
|
+
return {
|
|
2195
|
+
enabled,
|
|
2196
|
+
rearmRemainingThreshold: normalizedRearm,
|
|
2197
|
+
switchRemainingThreshold: normalizedSwitch,
|
|
2198
|
+
restartOfficialCodexOnAutoRoll: raw?.restartOfficialCodexOnAutoRoll === true ? true : DEFAULT_RESTART_OFFICIAL_CODEX_ON_AUTO_ROLL,
|
|
2199
|
+
launchOfficialCodexWhenClosedOnAutoRoll: raw?.launchOfficialCodexWhenClosedOnAutoRoll === true ? true : DEFAULT_LAUNCH_OFFICIAL_CODEX_WHEN_CLOSED_ON_AUTO_ROLL,
|
|
2200
|
+
priorityOrder: sanitizeAutoRollPriorityOrder(raw?.priorityOrder),
|
|
2201
|
+
lowRemainingNotificationEnabled: raw?.lowRemainingNotificationEnabled === true ? true : DEFAULT_LOW_REMAINING_NOTIFICATION_ENABLED,
|
|
2202
|
+
lowRemainingNotificationThreshold: sanitizeLowRemainingNotificationThreshold(
|
|
2203
|
+
typeof raw?.lowRemainingNotificationThreshold === "number" ? raw.lowRemainingNotificationThreshold : Number.NaN
|
|
2204
|
+
)
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2097
2208
|
// ../../packages/runtime-app-state/src/storage/documents.ts
|
|
2098
2209
|
var import_node_fs = require("fs");
|
|
2099
2210
|
var import_node_path = __toESM(require("path"), 1);
|
|
@@ -2351,9 +2462,23 @@ function createDefaultAppState() {
|
|
|
2351
2462
|
schemaVersion: 1,
|
|
2352
2463
|
autoRoll: {
|
|
2353
2464
|
enabled: false,
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
restartOfficialCodexOnAutoRoll: false
|
|
2465
|
+
rearmRemainingThreshold: 15,
|
|
2466
|
+
switchRemainingThreshold: 5,
|
|
2467
|
+
restartOfficialCodexOnAutoRoll: false,
|
|
2468
|
+
launchOfficialCodexWhenClosedOnAutoRoll: false,
|
|
2469
|
+
priorityOrder: [],
|
|
2470
|
+
lowRemainingNotificationEnabled: false,
|
|
2471
|
+
lowRemainingNotificationThreshold: 1
|
|
2472
|
+
},
|
|
2473
|
+
officialCodex: {
|
|
2474
|
+
lastProfileSwitchAt: null,
|
|
2475
|
+
lastProfileSwitchProfileKey: null,
|
|
2476
|
+
lastVerifiedLaunchAt: null,
|
|
2477
|
+
lastVerifiedLaunchProfileKey: null,
|
|
2478
|
+
lastObservedPid: null,
|
|
2479
|
+
lastRestartStatus: null,
|
|
2480
|
+
lastRestartReason: null,
|
|
2481
|
+
instancesByProfileName: {}
|
|
2357
2482
|
},
|
|
2358
2483
|
app: {
|
|
2359
2484
|
lastAppVersion: null,
|
|
@@ -2454,34 +2579,72 @@ function asString(value) {
|
|
|
2454
2579
|
const trimmed = value.trim();
|
|
2455
2580
|
return trimmed.length > 0 ? trimmed : null;
|
|
2456
2581
|
}
|
|
2582
|
+
function asNumberOrNull(value) {
|
|
2583
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
2584
|
+
}
|
|
2457
2585
|
function normalizeAppState(raw) {
|
|
2458
2586
|
const defaults = createDefaultAppState();
|
|
2459
2587
|
if (!isRecord2(raw)) {
|
|
2460
2588
|
return defaults;
|
|
2461
2589
|
}
|
|
2590
|
+
const rawAutoRoll = isRecord2(raw.autoRoll) ? raw.autoRoll : void 0;
|
|
2462
2591
|
const next = deepMerge(defaults, raw);
|
|
2463
2592
|
const merged = clone2(next);
|
|
2464
2593
|
merged.schemaVersion = 1;
|
|
2465
|
-
|
|
2466
|
-
|
|
2594
|
+
merged.autoRoll = normalizeAutoRollSettings(rawAutoRoll);
|
|
2595
|
+
if (!isRecord2(merged.officialCodex)) {
|
|
2596
|
+
merged.officialCodex = clone2(defaults.officialCodex);
|
|
2467
2597
|
}
|
|
2468
|
-
|
|
2469
|
-
merged.
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
merged.
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
merged.
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
merged.
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
merged.
|
|
2598
|
+
merged.officialCodex.lastProfileSwitchAt = asNumberOrNull(
|
|
2599
|
+
merged.officialCodex.lastProfileSwitchAt
|
|
2600
|
+
);
|
|
2601
|
+
merged.officialCodex.lastProfileSwitchProfileKey = asString(
|
|
2602
|
+
merged.officialCodex.lastProfileSwitchProfileKey
|
|
2603
|
+
);
|
|
2604
|
+
merged.officialCodex.lastVerifiedLaunchAt = asNumberOrNull(
|
|
2605
|
+
merged.officialCodex.lastVerifiedLaunchAt
|
|
2606
|
+
);
|
|
2607
|
+
merged.officialCodex.lastVerifiedLaunchProfileKey = asString(
|
|
2608
|
+
merged.officialCodex.lastVerifiedLaunchProfileKey
|
|
2609
|
+
);
|
|
2610
|
+
merged.officialCodex.lastObservedPid = asNumberOrNull(
|
|
2611
|
+
merged.officialCodex.lastObservedPid
|
|
2612
|
+
);
|
|
2613
|
+
merged.officialCodex.lastRestartStatus = asString(
|
|
2614
|
+
merged.officialCodex.lastRestartStatus
|
|
2615
|
+
);
|
|
2616
|
+
merged.officialCodex.lastRestartReason = asString(
|
|
2617
|
+
merged.officialCodex.lastRestartReason
|
|
2618
|
+
);
|
|
2619
|
+
if (!isRecord2(merged.officialCodex.instancesByProfileName)) {
|
|
2620
|
+
merged.officialCodex.instancesByProfileName = {};
|
|
2621
|
+
} else {
|
|
2622
|
+
const nextInstances = {};
|
|
2623
|
+
for (const [key, value] of Object.entries(
|
|
2624
|
+
merged.officialCodex.instancesByProfileName
|
|
2625
|
+
)) {
|
|
2626
|
+
if (!isRecord2(value)) {
|
|
2627
|
+
continue;
|
|
2628
|
+
}
|
|
2629
|
+
const profileName = asString(value.profileName) ?? asString(key);
|
|
2630
|
+
if (!profileName) {
|
|
2631
|
+
continue;
|
|
2632
|
+
}
|
|
2633
|
+
nextInstances[profileName] = {
|
|
2634
|
+
profileName,
|
|
2635
|
+
profileKey: asString(value.profileKey),
|
|
2636
|
+
profileHome: asString(value.profileHome),
|
|
2637
|
+
appPath: asString(value.appPath),
|
|
2638
|
+
bundleId: asString(value.bundleId),
|
|
2639
|
+
pid: asNumberOrNull(value.pid),
|
|
2640
|
+
appServerPid: asNumberOrNull(value.appServerPid),
|
|
2641
|
+
launchedAt: asNumberOrNull(value.launchedAt),
|
|
2642
|
+
lastVerifiedAt: asNumberOrNull(value.lastVerifiedAt),
|
|
2643
|
+
lastStatus: asString(value.lastStatus),
|
|
2644
|
+
lastError: asString(value.lastError)
|
|
2645
|
+
};
|
|
2646
|
+
}
|
|
2647
|
+
merged.officialCodex.instancesByProfileName = nextInstances;
|
|
2485
2648
|
}
|
|
2486
2649
|
merged.app.lastAppVersion = asString(merged.app.lastAppVersion);
|
|
2487
2650
|
merged.app.pendingUpdateVersion = asString(merged.app.pendingUpdateVersion);
|
|
@@ -2977,59 +3140,6 @@ async function writeAppSettings(settings, onUpdated) {
|
|
|
2977
3140
|
// ../../packages/runtime-profiles/src/license/service.ts
|
|
2978
3141
|
var import_node_crypto2 = __toESM(require("crypto"), 1);
|
|
2979
3142
|
|
|
2980
|
-
// ../../packages/contracts/src/settings/auto-roll.ts
|
|
2981
|
-
var DEFAULT_AUTO_ROLL_ENABLED = false;
|
|
2982
|
-
var DEFAULT_AUTO_ROLL_WARNING_THRESHOLD = 85;
|
|
2983
|
-
var DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD = 95;
|
|
2984
|
-
var DEFAULT_RESTART_OFFICIAL_CODEX_ON_AUTO_ROLL = false;
|
|
2985
|
-
var AUTO_ROLL_WARNING_MIN = 50;
|
|
2986
|
-
var AUTO_ROLL_WARNING_MAX = 99;
|
|
2987
|
-
var AUTO_ROLL_SWITCH_MAX = 100;
|
|
2988
|
-
function clampNumber(value, min, max) {
|
|
2989
|
-
return Math.min(max, Math.max(min, value));
|
|
2990
|
-
}
|
|
2991
|
-
function resolveFiniteNumber(value, fallback) {
|
|
2992
|
-
return Number.isFinite(value) ? value : fallback;
|
|
2993
|
-
}
|
|
2994
|
-
function sanitizeAutoRollWarningThreshold(value) {
|
|
2995
|
-
const numeric = resolveFiniteNumber(value, DEFAULT_AUTO_ROLL_WARNING_THRESHOLD);
|
|
2996
|
-
return clampNumber(numeric, AUTO_ROLL_WARNING_MIN, AUTO_ROLL_WARNING_MAX);
|
|
2997
|
-
}
|
|
2998
|
-
function sanitizeAutoRollSwitchThreshold(value, warningThreshold) {
|
|
2999
|
-
const sanitizedWarning = sanitizeAutoRollWarningThreshold(warningThreshold);
|
|
3000
|
-
const numeric = resolveFiniteNumber(value, DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD);
|
|
3001
|
-
return clampNumber(numeric, sanitizedWarning + 1, AUTO_ROLL_SWITCH_MAX);
|
|
3002
|
-
}
|
|
3003
|
-
function sanitizeAutoRollThresholds(warningThreshold, switchThreshold) {
|
|
3004
|
-
const sanitizedWarning = sanitizeAutoRollWarningThreshold(warningThreshold);
|
|
3005
|
-
const sanitizedSwitch = sanitizeAutoRollSwitchThreshold(switchThreshold, sanitizedWarning);
|
|
3006
|
-
return {
|
|
3007
|
-
warningThreshold: sanitizedWarning,
|
|
3008
|
-
switchThreshold: sanitizedSwitch
|
|
3009
|
-
};
|
|
3010
|
-
}
|
|
3011
|
-
function normalizeAutoRollSettings(raw) {
|
|
3012
|
-
const enabled = typeof raw?.enabled === "boolean" ? raw.enabled : DEFAULT_AUTO_ROLL_ENABLED;
|
|
3013
|
-
const rawWarning = resolveFiniteNumber(
|
|
3014
|
-
typeof raw?.warningThreshold === "number" ? raw.warningThreshold : Number.NaN,
|
|
3015
|
-
DEFAULT_AUTO_ROLL_WARNING_THRESHOLD
|
|
3016
|
-
);
|
|
3017
|
-
const rawSwitch = resolveFiniteNumber(
|
|
3018
|
-
typeof raw?.switchThreshold === "number" ? raw.switchThreshold : Number.NaN,
|
|
3019
|
-
DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD
|
|
3020
|
-
);
|
|
3021
|
-
const {
|
|
3022
|
-
warningThreshold: normalizedWarning,
|
|
3023
|
-
switchThreshold: normalizedSwitch
|
|
3024
|
-
} = sanitizeAutoRollThresholds(rawWarning, rawSwitch);
|
|
3025
|
-
return {
|
|
3026
|
-
enabled,
|
|
3027
|
-
warningThreshold: normalizedWarning,
|
|
3028
|
-
switchThreshold: normalizedSwitch,
|
|
3029
|
-
restartOfficialCodexOnAutoRoll: raw?.restartOfficialCodexOnAutoRoll === true ? true : DEFAULT_RESTART_OFFICIAL_CODEX_ON_AUTO_ROLL
|
|
3030
|
-
};
|
|
3031
|
-
}
|
|
3032
|
-
|
|
3033
3143
|
// ../../packages/runtime-codex/src/codex/settings.ts
|
|
3034
3144
|
function asString2(value) {
|
|
3035
3145
|
if (typeof value !== "string") {
|
|
@@ -3071,17 +3181,6 @@ function parseStoredLicense(raw) {
|
|
|
3071
3181
|
);
|
|
3072
3182
|
return hasValue ? license : null;
|
|
3073
3183
|
}
|
|
3074
|
-
async function getLastProfileName() {
|
|
3075
|
-
const state = await getAppState();
|
|
3076
|
-
return asString2(state.app.lastProfileName);
|
|
3077
|
-
}
|
|
3078
|
-
async function persistLastProfileName(profileName) {
|
|
3079
|
-
await patchAppState({
|
|
3080
|
-
app: {
|
|
3081
|
-
lastProfileName: asString2(profileName)
|
|
3082
|
-
}
|
|
3083
|
-
});
|
|
3084
|
-
}
|
|
3085
3184
|
async function getStoredLicense() {
|
|
3086
3185
|
const state = await getAppState();
|
|
3087
3186
|
return parseStoredLicense(state.license);
|
|
@@ -3150,9 +3249,13 @@ async function writeCodexSettingsJsonRaw(payload) {
|
|
|
3150
3249
|
},
|
|
3151
3250
|
autoRoll: autoRoll ? {
|
|
3152
3251
|
enabled: autoRoll.enabled,
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
restartOfficialCodexOnAutoRoll: autoRoll.restartOfficialCodexOnAutoRoll
|
|
3252
|
+
rearmRemainingThreshold: autoRoll.rearmRemainingThreshold,
|
|
3253
|
+
switchRemainingThreshold: autoRoll.switchRemainingThreshold,
|
|
3254
|
+
restartOfficialCodexOnAutoRoll: autoRoll.restartOfficialCodexOnAutoRoll,
|
|
3255
|
+
launchOfficialCodexWhenClosedOnAutoRoll: autoRoll.launchOfficialCodexWhenClosedOnAutoRoll,
|
|
3256
|
+
priorityOrder: autoRoll.priorityOrder,
|
|
3257
|
+
lowRemainingNotificationEnabled: autoRoll.lowRemainingNotificationEnabled,
|
|
3258
|
+
lowRemainingNotificationThreshold: autoRoll.lowRemainingNotificationThreshold
|
|
3156
3259
|
} : void 0,
|
|
3157
3260
|
license: license ? {
|
|
3158
3261
|
licenseKey: license.licenseKey ?? null,
|
|
@@ -4245,21 +4348,6 @@ var ProfileManager = class {
|
|
|
4245
4348
|
error && typeof error === "object" && "code" in error && error.code === "ENOENT"
|
|
4246
4349
|
);
|
|
4247
4350
|
}
|
|
4248
|
-
async readPreferredProfileName() {
|
|
4249
|
-
try {
|
|
4250
|
-
return await getLastProfileName();
|
|
4251
|
-
} catch (error) {
|
|
4252
|
-
logWarn("Failed to read preferred profile name:", error);
|
|
4253
|
-
return null;
|
|
4254
|
-
}
|
|
4255
|
-
}
|
|
4256
|
-
async persistPreferredProfileName(name) {
|
|
4257
|
-
try {
|
|
4258
|
-
await persistLastProfileName(name);
|
|
4259
|
-
} catch (error) {
|
|
4260
|
-
logWarn("Failed to persist preferred profile name:", error);
|
|
4261
|
-
}
|
|
4262
|
-
}
|
|
4263
4351
|
toStateRecord(record) {
|
|
4264
4352
|
return {
|
|
4265
4353
|
name: record.name,
|
|
@@ -4379,47 +4467,36 @@ var ProfileManager = class {
|
|
|
4379
4467
|
return null;
|
|
4380
4468
|
}
|
|
4381
4469
|
}
|
|
4382
|
-
|
|
4383
|
-
|
|
4470
|
+
emptyAuthSnapshot(fingerprint = null) {
|
|
4471
|
+
return {
|
|
4472
|
+
fingerprint,
|
|
4473
|
+
email: null,
|
|
4474
|
+
accountId: null,
|
|
4475
|
+
userId: null,
|
|
4476
|
+
chatgptUserId: null,
|
|
4477
|
+
chatgptAccountUserId: null,
|
|
4478
|
+
workspaceId: null
|
|
4479
|
+
};
|
|
4480
|
+
}
|
|
4481
|
+
async captureAuthSnapshotAtPath(authPath) {
|
|
4384
4482
|
let raw;
|
|
4385
4483
|
try {
|
|
4386
|
-
raw = await import_fs.promises.readFile(
|
|
4484
|
+
raw = await import_fs.promises.readFile(authPath, "utf8");
|
|
4387
4485
|
} catch (error) {
|
|
4388
4486
|
if (this.isNotFoundError(error)) {
|
|
4389
|
-
return
|
|
4390
|
-
fingerprint: null,
|
|
4391
|
-
email: null,
|
|
4392
|
-
accountId: null,
|
|
4393
|
-
userId: null,
|
|
4394
|
-
chatgptUserId: null,
|
|
4395
|
-
workspaceId: null
|
|
4396
|
-
};
|
|
4487
|
+
return this.emptyAuthSnapshot();
|
|
4397
4488
|
}
|
|
4398
4489
|
const message = error instanceof Error ? error.message : "unknown error";
|
|
4399
|
-
const signature = typeof message === "string" ? `${message}:${
|
|
4490
|
+
const signature = typeof message === "string" ? `${message}:${authPath}` : authPath;
|
|
4400
4491
|
if (this.lastActiveAuthErrorSignature !== signature) {
|
|
4401
|
-
logWarn("Failed to snapshot
|
|
4492
|
+
logWarn("Failed to snapshot auth file:", error);
|
|
4402
4493
|
this.lastActiveAuthErrorSignature = signature;
|
|
4403
4494
|
}
|
|
4404
|
-
return
|
|
4405
|
-
fingerprint: null,
|
|
4406
|
-
email: null,
|
|
4407
|
-
accountId: null,
|
|
4408
|
-
userId: null,
|
|
4409
|
-
chatgptUserId: null,
|
|
4410
|
-
workspaceId: null
|
|
4411
|
-
};
|
|
4495
|
+
return this.emptyAuthSnapshot();
|
|
4412
4496
|
}
|
|
4413
4497
|
const trimmed = raw.trim();
|
|
4414
4498
|
if (!trimmed) {
|
|
4415
|
-
return
|
|
4416
|
-
fingerprint: null,
|
|
4417
|
-
email: null,
|
|
4418
|
-
accountId: null,
|
|
4419
|
-
userId: null,
|
|
4420
|
-
chatgptUserId: null,
|
|
4421
|
-
workspaceId: null
|
|
4422
|
-
};
|
|
4499
|
+
return this.emptyAuthSnapshot();
|
|
4423
4500
|
}
|
|
4424
4501
|
const fingerprint = (0, import_node_crypto3.createHash)("sha256").update(trimmed).digest("hex");
|
|
4425
4502
|
try {
|
|
@@ -4433,19 +4510,27 @@ var ProfileManager = class {
|
|
|
4433
4510
|
accountId: this.getAccountIdFromData(normalized) ?? null,
|
|
4434
4511
|
userId: metadata?.userId ?? null,
|
|
4435
4512
|
chatgptUserId: metadata?.chatgptUserId ?? null,
|
|
4513
|
+
chatgptAccountUserId: metadata?.chatgptAccountUserId ?? null,
|
|
4436
4514
|
workspaceId: workspace.id ?? null
|
|
4437
4515
|
};
|
|
4438
4516
|
} catch {
|
|
4439
|
-
return
|
|
4440
|
-
fingerprint,
|
|
4441
|
-
email: null,
|
|
4442
|
-
accountId: null,
|
|
4443
|
-
userId: null,
|
|
4444
|
-
chatgptUserId: null,
|
|
4445
|
-
workspaceId: null
|
|
4446
|
-
};
|
|
4517
|
+
return this.emptyAuthSnapshot(fingerprint);
|
|
4447
4518
|
}
|
|
4448
4519
|
}
|
|
4520
|
+
async captureAuthSnapshot(authPath) {
|
|
4521
|
+
const targetPath = authPath ?? this.activeAuth;
|
|
4522
|
+
if (targetPath === this.activeAuth) {
|
|
4523
|
+
return this.enqueueAuthSwap(async () => {
|
|
4524
|
+
await this.initialize();
|
|
4525
|
+
return this.captureAuthSnapshotAtPath(targetPath);
|
|
4526
|
+
});
|
|
4527
|
+
}
|
|
4528
|
+
await this.initialize();
|
|
4529
|
+
return this.captureAuthSnapshotAtPath(targetPath);
|
|
4530
|
+
}
|
|
4531
|
+
async captureActiveAuthSnapshot() {
|
|
4532
|
+
return this.captureAuthSnapshot(this.activeAuth);
|
|
4533
|
+
}
|
|
4449
4534
|
/**
|
|
4450
4535
|
* Decode a JWT payload into an object without validating the signature.
|
|
4451
4536
|
*/
|
|
@@ -4842,6 +4927,130 @@ var ProfileManager = class {
|
|
|
4842
4927
|
metadata: record.metadata
|
|
4843
4928
|
}) ?? resolveFallbackAccountKey(record.name);
|
|
4844
4929
|
}
|
|
4930
|
+
describeActiveAuth(auth) {
|
|
4931
|
+
const normalized = this.normalizeProfileData(auth);
|
|
4932
|
+
if (typeof normalized.auth_method === "string") {
|
|
4933
|
+
normalized.auth_method = normalized.auth_method.trim().toLowerCase();
|
|
4934
|
+
}
|
|
4935
|
+
normalized.auth_method = normalized.auth_method ?? "codex-cli";
|
|
4936
|
+
const metadata = this.extractProfileMetadata(normalized);
|
|
4937
|
+
const workspace = this.resolveWorkspaceIdentity(normalized, metadata);
|
|
4938
|
+
const workspaceId = workspace.id || DEFAULT_WORKSPACE_ID;
|
|
4939
|
+
const workspaceName = workspace.name ?? null;
|
|
4940
|
+
normalized.workspace_id = normalized.workspace_id ?? workspaceId;
|
|
4941
|
+
if (workspaceName) {
|
|
4942
|
+
normalized.workspace_name = normalized.workspace_name ?? workspaceName;
|
|
4943
|
+
}
|
|
4944
|
+
const email = this.resolveProfileEmail(normalized, metadata) ?? null;
|
|
4945
|
+
if (email) {
|
|
4946
|
+
normalized.email = email;
|
|
4947
|
+
}
|
|
4948
|
+
const accountId = this.getAccountIdFromData(normalized) ?? null;
|
|
4949
|
+
const identityKey = this.resolveProfileIdentityFromData(null, normalized, metadata);
|
|
4950
|
+
const hasTokenPayload = Boolean(
|
|
4951
|
+
normalized.id_token || normalized.access_token || normalized.refresh_token || accountId || email
|
|
4952
|
+
);
|
|
4953
|
+
return {
|
|
4954
|
+
normalized,
|
|
4955
|
+
metadata,
|
|
4956
|
+
workspaceId,
|
|
4957
|
+
workspaceName,
|
|
4958
|
+
email,
|
|
4959
|
+
accountId,
|
|
4960
|
+
identityKey,
|
|
4961
|
+
hasTokenPayload
|
|
4962
|
+
};
|
|
4963
|
+
}
|
|
4964
|
+
async findProfileForActiveAuth(identityKey, workspaceId) {
|
|
4965
|
+
return this.getProfileRowByIdentityAndWorkspace(identityKey, workspaceId, {
|
|
4966
|
+
preferAuthMethod: "codex-cli"
|
|
4967
|
+
});
|
|
4968
|
+
}
|
|
4969
|
+
async syncMatchedProfileFromActiveAuth(record, description) {
|
|
4970
|
+
if (!this.hasTokenChanges(record.data, description.normalized)) {
|
|
4971
|
+
return;
|
|
4972
|
+
}
|
|
4973
|
+
const { profile: merged, metadata } = this.mergeProfileRecords(record.data, description.normalized);
|
|
4974
|
+
delete merged.tokenAlert;
|
|
4975
|
+
await this.persistProfileRecord(record.name, merged, metadata);
|
|
4976
|
+
}
|
|
4977
|
+
async assertActiveAuthMatchesRecord(record, message) {
|
|
4978
|
+
const activeAuth = await this.readActiveAuthFile();
|
|
4979
|
+
if (!activeAuth) {
|
|
4980
|
+
throw new Error("Active Codex auth is missing. Refresh profiles and try again.");
|
|
4981
|
+
}
|
|
4982
|
+
const activeDescription = this.describeActiveAuth(activeAuth);
|
|
4983
|
+
const targetIdentity = this.resolveProfileIdentityFromRecord(record);
|
|
4984
|
+
const targetWorkspaceId = this.normalizeWorkspaceId(record.workspaceId ?? record.data.workspace_id);
|
|
4985
|
+
if (activeDescription.identityKey !== targetIdentity || this.normalizeWorkspaceId(activeDescription.workspaceId) !== targetWorkspaceId) {
|
|
4986
|
+
throw new Error(message);
|
|
4987
|
+
}
|
|
4988
|
+
}
|
|
4989
|
+
async writeActiveAuthForRecord(record) {
|
|
4990
|
+
const profileData = record.data;
|
|
4991
|
+
const targetIdentity = this.resolveProfileIdentityFromRecord(record);
|
|
4992
|
+
const targetWorkspaceId = this.normalizeWorkspaceId(record.workspaceId ?? profileData.workspace_id);
|
|
4993
|
+
const codexFormat = this.convertToCodexFormat(profileData);
|
|
4994
|
+
await this.writeAtomic(this.activeAuth, JSON.stringify(codexFormat, null, 2));
|
|
4995
|
+
const written = await this.readActiveAuthFile();
|
|
4996
|
+
if (!written) {
|
|
4997
|
+
throw new Error("Codex auth file was not written.");
|
|
4998
|
+
}
|
|
4999
|
+
const writtenDescription = this.describeActiveAuth(written);
|
|
5000
|
+
if (writtenDescription.identityKey !== targetIdentity) {
|
|
5001
|
+
throw new Error("Codex auth verification failed after profile switch.");
|
|
5002
|
+
}
|
|
5003
|
+
if (this.normalizeWorkspaceId(writtenDescription.workspaceId) !== targetWorkspaceId) {
|
|
5004
|
+
throw new Error("Codex auth workspace verification failed after profile switch.");
|
|
5005
|
+
}
|
|
5006
|
+
}
|
|
5007
|
+
async syncActiveAuthFromProfileIfCurrent(name) {
|
|
5008
|
+
const profileName = this.normalizeProfileName(name);
|
|
5009
|
+
const record = await this.getProfileRowByName(profileName);
|
|
5010
|
+
if (!record) {
|
|
5011
|
+
return;
|
|
5012
|
+
}
|
|
5013
|
+
const activeAuth = await this.readActiveAuthFile();
|
|
5014
|
+
if (!activeAuth) {
|
|
5015
|
+
return;
|
|
5016
|
+
}
|
|
5017
|
+
const activeDescription = this.describeActiveAuth(activeAuth);
|
|
5018
|
+
const targetIdentity = this.resolveProfileIdentityFromRecord(record);
|
|
5019
|
+
const targetWorkspaceId = this.normalizeWorkspaceId(record.workspaceId ?? record.data.workspace_id);
|
|
5020
|
+
if (activeDescription.identityKey !== targetIdentity || this.normalizeWorkspaceId(activeDescription.workspaceId) !== targetWorkspaceId) {
|
|
5021
|
+
return;
|
|
5022
|
+
}
|
|
5023
|
+
await this.writeActiveAuthForRecord(record);
|
|
5024
|
+
}
|
|
5025
|
+
async removeActiveAuthFiles() {
|
|
5026
|
+
await import_fs.promises.rm(this.activeAuth, { force: true });
|
|
5027
|
+
await import_fs.promises.rm(this.activeAuthBackup, { force: true });
|
|
5028
|
+
await import_fs.promises.rm(`${this.activeAuth}.tmp`, { force: true });
|
|
5029
|
+
}
|
|
5030
|
+
resolveAutoImportedProfileName(email, existingRecords) {
|
|
5031
|
+
const source = email ? email.split("@")[0] : "codex-account";
|
|
5032
|
+
const base = source.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "codex-account";
|
|
5033
|
+
const used = new Set(existingRecords.map((record) => record.name));
|
|
5034
|
+
let candidate = this.normalizeProfileName(base);
|
|
5035
|
+
let suffix = 2;
|
|
5036
|
+
while (used.has(candidate)) {
|
|
5037
|
+
candidate = this.normalizeProfileName(`${base}-${suffix}`);
|
|
5038
|
+
suffix += 1;
|
|
5039
|
+
}
|
|
5040
|
+
return candidate;
|
|
5041
|
+
}
|
|
5042
|
+
buildActiveCodexAuthStatus(state, description, options) {
|
|
5043
|
+
return {
|
|
5044
|
+
state,
|
|
5045
|
+
profileName: options?.profileName ?? null,
|
|
5046
|
+
email: description?.email ?? null,
|
|
5047
|
+
accountId: description?.accountId ?? null,
|
|
5048
|
+
workspaceId: description?.workspaceId ?? null,
|
|
5049
|
+
workspaceName: description?.workspaceName ?? null,
|
|
5050
|
+
importedProfileName: options?.importedProfileName ?? null,
|
|
5051
|
+
importBlockedReason: options?.importBlockedReason ?? null
|
|
5052
|
+
};
|
|
5053
|
+
}
|
|
4845
5054
|
resolveWorkspaceIdentity(data, metadata) {
|
|
4846
5055
|
const directId = "workspace_id" in data && typeof data.workspace_id === "string" ? data.workspace_id.trim() : void 0;
|
|
4847
5056
|
const directName = "workspace_name" in data && typeof data.workspace_name === "string" ? data.workspace_name.trim() : void 0;
|
|
@@ -4862,22 +5071,6 @@ var ProfileManager = class {
|
|
|
4862
5071
|
const record = await this.readProfileRecord(normalized);
|
|
4863
5072
|
return record ?? void 0;
|
|
4864
5073
|
}
|
|
4865
|
-
async getProfileRowByIdentityKey(identityKey, options) {
|
|
4866
|
-
const records = await this.listProfileRecords();
|
|
4867
|
-
const matches = records.filter((record) => this.resolveProfileIdentityFromRecord(record) === identityKey);
|
|
4868
|
-
if (matches.length === 0) {
|
|
4869
|
-
return void 0;
|
|
4870
|
-
}
|
|
4871
|
-
const mustMatch = typeof options?.authMethod === "string" ? options.authMethod.trim().toLowerCase() : null;
|
|
4872
|
-
if (mustMatch) {
|
|
4873
|
-
return matches.find((record) => this.resolveAuthMethod(record.data) === mustMatch);
|
|
4874
|
-
}
|
|
4875
|
-
const prefer = typeof options?.preferAuthMethod === "string" ? options.preferAuthMethod.trim().toLowerCase() : null;
|
|
4876
|
-
if (prefer) {
|
|
4877
|
-
return matches.find((record) => this.resolveAuthMethod(record.data) === prefer) ?? matches[0];
|
|
4878
|
-
}
|
|
4879
|
-
return matches[0];
|
|
4880
|
-
}
|
|
4881
5074
|
async getProfileRowByIdentityAndWorkspace(identityKey, workspaceId, options) {
|
|
4882
5075
|
const workspaceKey = workspaceId && workspaceId.trim().length > 0 ? workspaceId.trim() : DEFAULT_WORKSPACE_ID;
|
|
4883
5076
|
const records = await this.listProfileRecords();
|
|
@@ -4901,6 +5094,9 @@ var ProfileManager = class {
|
|
|
4901
5094
|
}
|
|
4902
5095
|
return matches[0];
|
|
4903
5096
|
}
|
|
5097
|
+
normalizeWorkspaceId(value) {
|
|
5098
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : DEFAULT_WORKSPACE_ID;
|
|
5099
|
+
}
|
|
4904
5100
|
async persistProfileRecord(name, data, metadata, options) {
|
|
4905
5101
|
const resolvedName = this.normalizeProfileName(name);
|
|
4906
5102
|
const existingRecord = await this.readProfileRecord(resolvedName);
|
|
@@ -4960,6 +5156,22 @@ var ProfileManager = class {
|
|
|
4960
5156
|
});
|
|
4961
5157
|
return profile;
|
|
4962
5158
|
}
|
|
5159
|
+
async deleteProfileRecordAndHome(record) {
|
|
5160
|
+
const profileName = this.normalizeProfileName(record.name);
|
|
5161
|
+
if (!record.accountId) {
|
|
5162
|
+
const dashboardState = await this.readProfileDashboardState();
|
|
5163
|
+
delete dashboardState.customGroupsByAccountKey[resolveFallbackAccountKey(profileName)];
|
|
5164
|
+
await this.writeProfileDashboardState(dashboardState);
|
|
5165
|
+
}
|
|
5166
|
+
try {
|
|
5167
|
+
await import_fs.promises.rm(this.getProfileHomePath(profileName), { recursive: true, force: true });
|
|
5168
|
+
} catch (error) {
|
|
5169
|
+
if (!this.isNotFoundError(error)) {
|
|
5170
|
+
logWarn(`Failed to remove profile home for '${profileName}':`, error);
|
|
5171
|
+
}
|
|
5172
|
+
}
|
|
5173
|
+
await this.deleteProfileRecord(profileName);
|
|
5174
|
+
}
|
|
4963
5175
|
buildProfileFromData(name, data, fallback) {
|
|
4964
5176
|
const metadata = fallback?.metadata ?? this.extractProfileMetadata(data);
|
|
4965
5177
|
const workspace = this.resolveWorkspaceIdentity(data, metadata);
|
|
@@ -5019,6 +5231,18 @@ var ProfileManager = class {
|
|
|
5019
5231
|
if (normalizedLastRefresh) {
|
|
5020
5232
|
codexAuth.last_refresh = normalizedLastRefresh;
|
|
5021
5233
|
}
|
|
5234
|
+
const email = typeof data.email === "string" ? data.email.trim() : "";
|
|
5235
|
+
if (email) {
|
|
5236
|
+
codexAuth.email = email;
|
|
5237
|
+
}
|
|
5238
|
+
const workspaceId = typeof data.workspace_id === "string" ? data.workspace_id.trim() : "";
|
|
5239
|
+
if (workspaceId) {
|
|
5240
|
+
codexAuth.workspace_id = workspaceId;
|
|
5241
|
+
}
|
|
5242
|
+
const workspaceName = typeof data.workspace_name === "string" ? data.workspace_name.trim() : "";
|
|
5243
|
+
if (workspaceName) {
|
|
5244
|
+
codexAuth.workspace_name = workspaceName;
|
|
5245
|
+
}
|
|
5022
5246
|
return codexAuth;
|
|
5023
5247
|
}
|
|
5024
5248
|
/**
|
|
@@ -5041,8 +5265,17 @@ var ProfileManager = class {
|
|
|
5041
5265
|
if (normalizedLastRefresh) {
|
|
5042
5266
|
profile.last_refresh = normalizedLastRefresh;
|
|
5043
5267
|
}
|
|
5044
|
-
|
|
5045
|
-
|
|
5268
|
+
const email = typeof data.email === "string" ? data.email.trim() : "";
|
|
5269
|
+
if (email) {
|
|
5270
|
+
profile.email = email;
|
|
5271
|
+
}
|
|
5272
|
+
const workspaceId = typeof data.workspace_id === "string" ? data.workspace_id.trim() : "";
|
|
5273
|
+
if (workspaceId) {
|
|
5274
|
+
profile.workspace_id = workspaceId;
|
|
5275
|
+
}
|
|
5276
|
+
const workspaceName = typeof data.workspace_name === "string" ? data.workspace_name.trim() : "";
|
|
5277
|
+
if (workspaceName) {
|
|
5278
|
+
profile.workspace_name = workspaceName;
|
|
5046
5279
|
}
|
|
5047
5280
|
return profile;
|
|
5048
5281
|
}
|
|
@@ -5174,6 +5407,12 @@ var ProfileManager = class {
|
|
|
5174
5407
|
);
|
|
5175
5408
|
return;
|
|
5176
5409
|
}
|
|
5410
|
+
if (this.normalizeWorkspaceId(existing.workspace_id) !== this.normalizeWorkspaceId(normalized.workspace_id)) {
|
|
5411
|
+
logWarn(
|
|
5412
|
+
`Skipped syncing tokens for profile '${profileName}' because Codex auth switched to a different workspace.`
|
|
5413
|
+
);
|
|
5414
|
+
return;
|
|
5415
|
+
}
|
|
5177
5416
|
if (!this.hasTokenChanges(existing, normalized)) {
|
|
5178
5417
|
return;
|
|
5179
5418
|
}
|
|
@@ -5371,94 +5610,80 @@ var ProfileManager = class {
|
|
|
5371
5610
|
}
|
|
5372
5611
|
if (finalAuthContent) {
|
|
5373
5612
|
await this.syncProfileTokensFromAuthContent(profileName, record.data, finalAuthContent);
|
|
5613
|
+
await this.syncActiveAuthFromProfileIfCurrent(profileName);
|
|
5374
5614
|
return;
|
|
5375
5615
|
}
|
|
5376
5616
|
await this.syncProfileTokensFromActiveAuth(profileName, record.data, authPath);
|
|
5617
|
+
await this.syncActiveAuthFromProfileIfCurrent(profileName);
|
|
5377
5618
|
})
|
|
5378
5619
|
);
|
|
5379
5620
|
}
|
|
5380
5621
|
/**
|
|
5381
5622
|
* Create a new profile by running codex login
|
|
5382
5623
|
*/
|
|
5383
|
-
async
|
|
5384
|
-
|
|
5385
|
-
const profileName = this.normalizeProfileName(name);
|
|
5386
|
-
if (await this.getProfileRowByName(profileName)) {
|
|
5387
|
-
throw new Error(`Profile '${profileName}' already exists!`);
|
|
5388
|
-
}
|
|
5389
|
-
let authData;
|
|
5624
|
+
async readAuthFileForImport(authPath, actionLabel) {
|
|
5625
|
+
let authRaw;
|
|
5390
5626
|
try {
|
|
5391
|
-
|
|
5392
|
-
authData = JSON.parse(authRaw);
|
|
5627
|
+
authRaw = await import_fs.promises.readFile(authPath, "utf8");
|
|
5393
5628
|
} catch (error) {
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
if (typeof normalizedProfile.auth_method === "string") {
|
|
5400
|
-
normalizedProfile.auth_method = normalizedProfile.auth_method.trim().toLowerCase();
|
|
5401
|
-
}
|
|
5402
|
-
normalizedProfile.auth_method = normalizedProfile.auth_method ?? "codex-cli";
|
|
5403
|
-
const metadata = this.extractProfileMetadata(normalizedProfile);
|
|
5404
|
-
const workspace = this.resolveWorkspaceIdentity(normalizedProfile, metadata);
|
|
5405
|
-
normalizedProfile.workspace_id = normalizedProfile.workspace_id ?? workspace.id ?? DEFAULT_WORKSPACE_ID;
|
|
5406
|
-
if (workspace.name) {
|
|
5407
|
-
normalizedProfile.workspace_name = normalizedProfile.workspace_name ?? workspace.name;
|
|
5408
|
-
}
|
|
5409
|
-
const resolvedEmail = this.resolveProfileEmail(normalizedProfile, metadata);
|
|
5410
|
-
if (resolvedEmail) {
|
|
5411
|
-
normalizedProfile.email = resolvedEmail;
|
|
5629
|
+
if (this.isNotFoundError(error)) {
|
|
5630
|
+
throw new Error(`Codex CLI did not produce an auth file. Complete the login before ${actionLabel}.`);
|
|
5631
|
+
}
|
|
5632
|
+
logError(`Failed to read Codex auth file during ${actionLabel}:`, error);
|
|
5633
|
+
throw new Error("Failed to read Codex auth file. Complete the login and try again.");
|
|
5412
5634
|
}
|
|
5413
5635
|
try {
|
|
5414
|
-
|
|
5415
|
-
const stored = await this.getProfileRowByName(profileName);
|
|
5416
|
-
return stored ? this.buildProfileFromRow(stored) : null;
|
|
5636
|
+
return JSON.parse(authRaw);
|
|
5417
5637
|
} catch (error) {
|
|
5418
|
-
logError(
|
|
5419
|
-
throw new Error("Failed to
|
|
5638
|
+
logError(`Failed to parse Codex auth file during ${actionLabel}:`, error);
|
|
5639
|
+
throw new Error("Failed to parse Codex auth file. Complete the login and try again.");
|
|
5420
5640
|
}
|
|
5421
5641
|
}
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
await this.
|
|
5642
|
+
async persistNewProfileFromAuth(profileName, authData) {
|
|
5643
|
+
const description = this.describeActiveAuth(authData);
|
|
5644
|
+
const normalizedProfile = description.normalized;
|
|
5645
|
+
normalizedProfile.created_at = normalizedProfile.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
5646
|
+
await this.persistProfileRecord(profileName, normalizedProfile, description.metadata);
|
|
5647
|
+
const stored = await this.getProfileRowByName(profileName);
|
|
5648
|
+
return stored ? this.buildProfileFromRow(stored) : null;
|
|
5649
|
+
}
|
|
5650
|
+
async createProfile(name) {
|
|
5427
5651
|
const profileName = this.normalizeProfileName(name);
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5652
|
+
return this.enqueueProfileOperation(
|
|
5653
|
+
profileName,
|
|
5654
|
+
() => this.enqueueAuthSwap(async () => {
|
|
5655
|
+
await this.initialize();
|
|
5656
|
+
if (await this.getProfileRowByName(profileName)) {
|
|
5657
|
+
throw new Error(`Profile '${profileName}' already exists!`);
|
|
5658
|
+
}
|
|
5659
|
+
try {
|
|
5660
|
+
const authData = await this.readAuthFileForImport(this.activeAuth, "profile creation");
|
|
5661
|
+
return await this.persistNewProfileFromAuth(profileName, authData);
|
|
5662
|
+
} catch (error) {
|
|
5663
|
+
logError("Error creating profile:", error);
|
|
5664
|
+
throw new Error("Failed to create profile. Make sure Codex CLI is installed and you are logged in.");
|
|
5665
|
+
}
|
|
5666
|
+
})
|
|
5667
|
+
);
|
|
5442
5668
|
}
|
|
5443
|
-
async
|
|
5444
|
-
await this.initialize();
|
|
5669
|
+
async createProfileFromAuthFile(name, authPath) {
|
|
5445
5670
|
const profileName = this.normalizeProfileName(name);
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
const existing = record.data;
|
|
5451
|
-
let activeAuth;
|
|
5452
|
-
try {
|
|
5453
|
-
const authContent = await import_fs.promises.readFile(this.activeAuth, "utf8");
|
|
5454
|
-
activeAuth = JSON.parse(authContent);
|
|
5455
|
-
} catch (error) {
|
|
5456
|
-
if (this.isNotFoundError(error)) {
|
|
5457
|
-
throw new Error("Codex CLI did not produce an auth file. Complete the login before refreshing this profile.");
|
|
5671
|
+
return this.enqueueProfileOperation(profileName, async () => {
|
|
5672
|
+
await this.initialize();
|
|
5673
|
+
if (await this.getProfileRowByName(profileName)) {
|
|
5674
|
+
throw new Error(`Profile '${profileName}' already exists!`);
|
|
5458
5675
|
}
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5676
|
+
try {
|
|
5677
|
+
const authData = await this.readAuthFileForImport(authPath, "profile creation");
|
|
5678
|
+
return await this.persistNewProfileFromAuth(profileName, authData);
|
|
5679
|
+
} catch (error) {
|
|
5680
|
+
logError("Error creating profile:", error);
|
|
5681
|
+
throw new Error("Failed to create profile. Make sure Codex CLI is installed and you are logged in.");
|
|
5682
|
+
}
|
|
5683
|
+
});
|
|
5684
|
+
}
|
|
5685
|
+
async refreshProfileAuthWithData(profileName, record, activeAuth) {
|
|
5686
|
+
const existing = record.data;
|
|
5462
5687
|
const normalized = this.normalizeProfileData(activeAuth);
|
|
5463
5688
|
const existingMetadata = record.metadata ?? this.extractProfileMetadata(existing);
|
|
5464
5689
|
const currentWorkspace = this.resolveWorkspaceIdentity(existing, existingMetadata);
|
|
@@ -5507,6 +5732,60 @@ var ProfileManager = class {
|
|
|
5507
5732
|
}
|
|
5508
5733
|
return profile;
|
|
5509
5734
|
}
|
|
5735
|
+
/**
|
|
5736
|
+
* Switch to a different profile
|
|
5737
|
+
*/
|
|
5738
|
+
async switchToProfile(name) {
|
|
5739
|
+
const profileName = this.normalizeProfileName(name);
|
|
5740
|
+
return this.enqueueProfileOperation(
|
|
5741
|
+
profileName,
|
|
5742
|
+
() => this.enqueueAuthSwap(async () => {
|
|
5743
|
+
await this.initialize();
|
|
5744
|
+
const record = await this.getProfileRowByName(profileName);
|
|
5745
|
+
if (!record) {
|
|
5746
|
+
throw new Error(`Profile '${profileName}' not found!`);
|
|
5747
|
+
}
|
|
5748
|
+
try {
|
|
5749
|
+
await this.writeActiveAuthForRecord(record);
|
|
5750
|
+
return true;
|
|
5751
|
+
} catch (error) {
|
|
5752
|
+
logError("Error switching profile:", error);
|
|
5753
|
+
throw new Error("Failed to switch profile");
|
|
5754
|
+
}
|
|
5755
|
+
})
|
|
5756
|
+
);
|
|
5757
|
+
}
|
|
5758
|
+
async refreshProfileAuth(name) {
|
|
5759
|
+
const profileName = this.normalizeProfileName(name);
|
|
5760
|
+
return this.enqueueProfileOperation(
|
|
5761
|
+
profileName,
|
|
5762
|
+
() => this.enqueueAuthSwap(async () => {
|
|
5763
|
+
await this.initialize();
|
|
5764
|
+
const record = await this.getProfileRowByName(profileName);
|
|
5765
|
+
if (!record) {
|
|
5766
|
+
throw new Error(`Profile '${profileName}' not found!`);
|
|
5767
|
+
}
|
|
5768
|
+
const activeAuth = await this.readAuthFileForImport(this.activeAuth, "profile refresh");
|
|
5769
|
+
return this.refreshProfileAuthWithData(profileName, record, activeAuth);
|
|
5770
|
+
})
|
|
5771
|
+
);
|
|
5772
|
+
}
|
|
5773
|
+
async refreshProfileAuthFromFile(name, authPath) {
|
|
5774
|
+
const profileName = this.normalizeProfileName(name);
|
|
5775
|
+
return this.enqueueProfileOperation(profileName, async () => {
|
|
5776
|
+
await this.initialize();
|
|
5777
|
+
const record = await this.getProfileRowByName(profileName);
|
|
5778
|
+
if (!record) {
|
|
5779
|
+
throw new Error(`Profile '${profileName}' not found!`);
|
|
5780
|
+
}
|
|
5781
|
+
const activeAuth = await this.readAuthFileForImport(authPath, "profile refresh");
|
|
5782
|
+
const profile = await this.refreshProfileAuthWithData(profileName, record, activeAuth);
|
|
5783
|
+
await this.enqueueAuthSwap(async () => {
|
|
5784
|
+
await this.syncActiveAuthFromProfileIfCurrent(profileName);
|
|
5785
|
+
});
|
|
5786
|
+
return profile;
|
|
5787
|
+
});
|
|
5788
|
+
}
|
|
5510
5789
|
/**
|
|
5511
5790
|
* Execute an action with a profile's auth injected.
|
|
5512
5791
|
* Creates an isolated CODEX_HOME with the profile's auth/config, invokes the action
|
|
@@ -5515,22 +5794,35 @@ var ProfileManager = class {
|
|
|
5515
5794
|
async runWithProfileAuth(name, action) {
|
|
5516
5795
|
return this.enqueueProfileOperation(
|
|
5517
5796
|
name,
|
|
5518
|
-
() => this.enqueueAuthSwap(
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5797
|
+
() => this.enqueueAuthSwap(async () => {
|
|
5798
|
+
try {
|
|
5799
|
+
return await this.runWithPreparedProfileHome(name, action, {
|
|
5800
|
+
syncFromActiveAuthBeforeAction: true
|
|
5801
|
+
});
|
|
5802
|
+
} finally {
|
|
5803
|
+
await this.syncActiveAuthFromProfileIfCurrent(name);
|
|
5804
|
+
}
|
|
5805
|
+
})
|
|
5523
5806
|
);
|
|
5524
5807
|
}
|
|
5525
5808
|
async readLiveRateLimits(name, options = {}) {
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
(
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5809
|
+
const profileName = this.normalizeProfileName(name);
|
|
5810
|
+
try {
|
|
5811
|
+
return await this.enqueueProfileOperation(
|
|
5812
|
+
profileName,
|
|
5813
|
+
() => this.runWithPreparedProfileHome(
|
|
5814
|
+
profileName,
|
|
5815
|
+
(env) => fetchRateLimitsViaRpc(env, { codexPath: options.codexPath }),
|
|
5816
|
+
{ syncFromActiveAuthBeforeAction: false }
|
|
5817
|
+
)
|
|
5818
|
+
);
|
|
5819
|
+
} finally {
|
|
5820
|
+
await this.enqueueAuthSwap(async () => {
|
|
5821
|
+
await this.syncActiveAuthFromProfileIfCurrent(profileName);
|
|
5822
|
+
}).catch((error) => {
|
|
5823
|
+
logWarn(`Failed to sync active auth after rate-limit probe for '${profileName}':`, error);
|
|
5824
|
+
});
|
|
5825
|
+
}
|
|
5534
5826
|
}
|
|
5535
5827
|
/**
|
|
5536
5828
|
* Rename a profile
|
|
@@ -5546,10 +5838,6 @@ var ProfileManager = class {
|
|
|
5546
5838
|
if (await this.getProfileRowByName(targetName)) {
|
|
5547
5839
|
throw new Error(`Profile '${targetName}' already exists!`);
|
|
5548
5840
|
}
|
|
5549
|
-
const preferred = await this.readPreferredProfileName();
|
|
5550
|
-
if (preferred && preferred === sourceName) {
|
|
5551
|
-
await this.persistPreferredProfileName(targetName);
|
|
5552
|
-
}
|
|
5553
5841
|
const oldHome = this.getProfileHomePath(sourceName);
|
|
5554
5842
|
const newHome = this.getProfileHomePath(targetName);
|
|
5555
5843
|
try {
|
|
@@ -5618,25 +5906,51 @@ var ProfileManager = class {
|
|
|
5618
5906
|
if (!record) {
|
|
5619
5907
|
throw new Error(`Profile '${profileName}' not found!`);
|
|
5620
5908
|
}
|
|
5621
|
-
|
|
5622
|
-
const dashboardState = await this.readProfileDashboardState();
|
|
5623
|
-
delete dashboardState.customGroupsByAccountKey[resolveFallbackAccountKey(profileName)];
|
|
5624
|
-
await this.writeProfileDashboardState(dashboardState);
|
|
5625
|
-
}
|
|
5626
|
-
const preferred = await this.readPreferredProfileName();
|
|
5627
|
-
if (preferred && preferred === profileName) {
|
|
5628
|
-
await this.persistPreferredProfileName(null);
|
|
5629
|
-
}
|
|
5630
|
-
try {
|
|
5631
|
-
await import_fs.promises.rm(this.getProfileHomePath(profileName), { recursive: true, force: true });
|
|
5632
|
-
} catch (error) {
|
|
5633
|
-
if (!this.isNotFoundError(error)) {
|
|
5634
|
-
logWarn(`Failed to remove profile home for '${profileName}':`, error);
|
|
5635
|
-
}
|
|
5636
|
-
}
|
|
5637
|
-
await this.deleteProfileRecord(profileName);
|
|
5909
|
+
await this.deleteProfileRecordAndHome(record);
|
|
5638
5910
|
return true;
|
|
5639
5911
|
}
|
|
5912
|
+
async deleteActiveProfileAndSwitch(name, replacementName) {
|
|
5913
|
+
const profileName = this.normalizeProfileName(name);
|
|
5914
|
+
const nextProfileName = this.normalizeProfileName(replacementName);
|
|
5915
|
+
if (profileName === nextProfileName) {
|
|
5916
|
+
throw new Error("Replacement profile must be different from the deleted profile.");
|
|
5917
|
+
}
|
|
5918
|
+
return this.enqueueAuthSwap(async () => {
|
|
5919
|
+
await this.initialize();
|
|
5920
|
+
const record = await this.getProfileRowByName(profileName);
|
|
5921
|
+
if (!record) {
|
|
5922
|
+
throw new Error(`Profile '${profileName}' not found!`);
|
|
5923
|
+
}
|
|
5924
|
+
const replacement = await this.getProfileRowByName(nextProfileName);
|
|
5925
|
+
if (!replacement) {
|
|
5926
|
+
throw new Error(`Profile '${nextProfileName}' not found!`);
|
|
5927
|
+
}
|
|
5928
|
+
await this.assertActiveAuthMatchesRecord(
|
|
5929
|
+
record,
|
|
5930
|
+
"Active Codex login changed before deletion. Refresh profiles and try again."
|
|
5931
|
+
);
|
|
5932
|
+
await this.writeActiveAuthForRecord(replacement);
|
|
5933
|
+
await this.deleteProfileRecordAndHome(record);
|
|
5934
|
+
return true;
|
|
5935
|
+
});
|
|
5936
|
+
}
|
|
5937
|
+
async deleteActiveProfileAndAuth(name) {
|
|
5938
|
+
const profileName = this.normalizeProfileName(name);
|
|
5939
|
+
return this.enqueueAuthSwap(async () => {
|
|
5940
|
+
await this.initialize();
|
|
5941
|
+
const record = await this.getProfileRowByName(profileName);
|
|
5942
|
+
if (!record) {
|
|
5943
|
+
throw new Error(`Profile '${profileName}' not found!`);
|
|
5944
|
+
}
|
|
5945
|
+
await this.assertActiveAuthMatchesRecord(
|
|
5946
|
+
record,
|
|
5947
|
+
"Active Codex login changed before deletion. Refresh profiles and try again."
|
|
5948
|
+
);
|
|
5949
|
+
await this.removeActiveAuthFiles();
|
|
5950
|
+
await this.deleteProfileRecordAndHome(record);
|
|
5951
|
+
return true;
|
|
5952
|
+
});
|
|
5953
|
+
}
|
|
5640
5954
|
/**
|
|
5641
5955
|
* Delete every profile that does not appear in the allow-list.
|
|
5642
5956
|
* Used to enforce plan limits when local storage was tampered with.
|
|
@@ -5673,64 +5987,61 @@ var ProfileManager = class {
|
|
|
5673
5987
|
}
|
|
5674
5988
|
}
|
|
5675
5989
|
}
|
|
5676
|
-
const preferred = await this.readPreferredProfileName();
|
|
5677
|
-
if (preferred && removed.includes(preferred)) {
|
|
5678
|
-
await this.persistPreferredProfileName(null);
|
|
5679
|
-
}
|
|
5680
5990
|
return removed;
|
|
5681
5991
|
}
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
if (preferredName) {
|
|
5689
|
-
try {
|
|
5690
|
-
const normalizedPreferred = this.normalizeProfileName(preferredName);
|
|
5691
|
-
const preferredRecord = await this.getProfileRowByName(normalizedPreferred);
|
|
5692
|
-
if (preferredRecord) {
|
|
5693
|
-
return { name: normalizedPreferred, trusted: true };
|
|
5694
|
-
}
|
|
5695
|
-
} catch {
|
|
5992
|
+
async getActiveCodexAuthStatus(options = {}) {
|
|
5993
|
+
return this.enqueueAuthSwap(async () => {
|
|
5994
|
+
await this.initialize();
|
|
5995
|
+
const activeAuth = await this.readActiveAuthFile();
|
|
5996
|
+
if (!activeAuth) {
|
|
5997
|
+
return this.buildActiveCodexAuthStatus("missing");
|
|
5696
5998
|
}
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
const
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5999
|
+
const description = this.describeActiveAuth(activeAuth);
|
|
6000
|
+
if (!description.hasTokenPayload || !description.identityKey) {
|
|
6001
|
+
return this.buildActiveCodexAuthStatus("unknown", description);
|
|
6002
|
+
}
|
|
6003
|
+
const matching = await this.findProfileForActiveAuth(
|
|
6004
|
+
description.identityKey,
|
|
6005
|
+
description.workspaceId
|
|
6006
|
+
);
|
|
6007
|
+
if (matching) {
|
|
6008
|
+
const normalized = this.normalizeProfileName(matching.name);
|
|
6009
|
+
await this.syncMatchedProfileFromActiveAuth(matching, description);
|
|
6010
|
+
return this.buildActiveCodexAuthStatus("matched", description, {
|
|
6011
|
+
profileName: normalized
|
|
5707
6012
|
});
|
|
5708
|
-
if (scoped) {
|
|
5709
|
-
const normalized = this.normalizeProfileName(scoped.name);
|
|
5710
|
-
await this.persistPreferredProfileName(normalized);
|
|
5711
|
-
return { name: normalized, trusted: true };
|
|
5712
|
-
}
|
|
5713
|
-
const matching = await this.getProfileRowByIdentityKey(activeIdentityKey, { preferAuthMethod: "codex-cli" });
|
|
5714
|
-
if (matching) {
|
|
5715
|
-
const normalized = this.normalizeProfileName(matching.name);
|
|
5716
|
-
await this.persistPreferredProfileName(normalized);
|
|
5717
|
-
return { name: normalized, trusted: true };
|
|
5718
|
-
}
|
|
5719
6013
|
}
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
return { name: normalized, trusted: true };
|
|
5728
|
-
}
|
|
5729
|
-
} catch {
|
|
5730
|
-
await this.persistPreferredProfileName(null);
|
|
5731
|
-
return { name: null, trusted: false };
|
|
6014
|
+
if (!options.autoImport) {
|
|
6015
|
+
return this.buildActiveCodexAuthStatus("unmanaged", description);
|
|
6016
|
+
}
|
|
6017
|
+
if (options.canAutoImport === false) {
|
|
6018
|
+
return this.buildActiveCodexAuthStatus("unmanaged", description, {
|
|
6019
|
+
importBlockedReason: options.importBlockedReason ?? "Profile limit reached. Upgrade to CodexUse Pro or remove a profile before importing this Codex login."
|
|
6020
|
+
});
|
|
5732
6021
|
}
|
|
5733
|
-
await this.
|
|
6022
|
+
const records = await this.listProfileRecords();
|
|
6023
|
+
const profileName = this.resolveAutoImportedProfileName(description.email, records);
|
|
6024
|
+
const profileData = {
|
|
6025
|
+
...description.normalized,
|
|
6026
|
+
auth_method: "codex-cli",
|
|
6027
|
+
created_at: description.normalized.created_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
6028
|
+
};
|
|
6029
|
+
await this.persistProfileRecord(profileName, profileData, description.metadata, {
|
|
6030
|
+
displayName: description.email ?? profileName
|
|
6031
|
+
});
|
|
6032
|
+
return this.buildActiveCodexAuthStatus("matched", description, {
|
|
6033
|
+
profileName,
|
|
6034
|
+
importedProfileName: profileName
|
|
6035
|
+
});
|
|
6036
|
+
});
|
|
6037
|
+
}
|
|
6038
|
+
/**
|
|
6039
|
+
* Check if the real Codex auth file matches a saved profile.
|
|
6040
|
+
*/
|
|
6041
|
+
async getCurrentProfile() {
|
|
6042
|
+
const status = await this.getActiveCodexAuthStatus({ autoImport: false });
|
|
6043
|
+
if (status.state === "matched" && status.profileName) {
|
|
6044
|
+
return { name: status.profileName, trusted: true };
|
|
5734
6045
|
}
|
|
5735
6046
|
return { name: null, trusted: false };
|
|
5736
6047
|
}
|
|
@@ -5831,17 +6142,6 @@ var ProfileManager = class {
|
|
|
5831
6142
|
}
|
|
5832
6143
|
}
|
|
5833
6144
|
}
|
|
5834
|
-
const preferred = await this.readPreferredProfileName();
|
|
5835
|
-
if (preferred) {
|
|
5836
|
-
try {
|
|
5837
|
-
const normalizedPreferred = this.normalizeProfileName(preferred);
|
|
5838
|
-
if (!normalized.has(normalizedPreferred)) {
|
|
5839
|
-
await this.persistPreferredProfileName(null);
|
|
5840
|
-
}
|
|
5841
|
-
} catch {
|
|
5842
|
-
await this.persistPreferredProfileName(null);
|
|
5843
|
-
}
|
|
5844
|
-
}
|
|
5845
6145
|
return {
|
|
5846
6146
|
imported: normalized.size,
|
|
5847
6147
|
removed
|
|
@@ -5874,8 +6174,8 @@ Usage:
|
|
|
5874
6174
|
codexuse profile current
|
|
5875
6175
|
codexuse profile add <name> [--skip-login] [--device-auth] [--login=browser|device]
|
|
5876
6176
|
codexuse profile refresh <name> [--skip-login] [--device-auth] [--login=browser|device]
|
|
5877
|
-
codexuse profile switch <name>
|
|
5878
|
-
codexuse profile autoroll [--threshold=50-100] [--dry-run] [--watch] [--interval=seconds]
|
|
6177
|
+
codexuse profile switch <name> [--restart-codex]
|
|
6178
|
+
codexuse profile autoroll [--switch-left=0-50] [--threshold=50-100] [--dry-run] [--watch] [--interval=seconds] [--restart-codex|--no-restart-codex]
|
|
5879
6179
|
codexuse profile delete <name>
|
|
5880
6180
|
codexuse profile rename <old> <new>
|
|
5881
6181
|
|
|
@@ -5895,22 +6195,26 @@ Flags:
|
|
|
5895
6195
|
--compact Names only
|
|
5896
6196
|
--device-auth Use device auth for Codex login
|
|
5897
6197
|
--login=MODE Login mode: browser | device
|
|
5898
|
-
--
|
|
6198
|
+
--switch-left=NN Auto-roll switch threshold by percent left (0-50)
|
|
6199
|
+
--threshold=NN Legacy auto-roll switch threshold by percent used (50-100)
|
|
5899
6200
|
--watch Keep checking and auto-switch when threshold is reached
|
|
5900
6201
|
--interval=SEC Watch interval in seconds (default: 30)
|
|
5901
6202
|
--dry-run Print planned switch without changing active profile
|
|
6203
|
+
--restart-codex Restart official Codex after a profile switch
|
|
6204
|
+
--no-restart-codex Override saved auto-roll restart setting
|
|
5902
6205
|
--profile=NAME Run Codex with one saved profile
|
|
5903
6206
|
--runtime=NAME Accounts Pool runtime store: desktop | daemon
|
|
5904
6207
|
--state-dir=PATH Override the runtime state dir for Accounts Pool inspection
|
|
5905
6208
|
--port=NNNN Stable daemon/API port (or use with account-pool status for daemon base URL)
|
|
5906
6209
|
--telegram-bot-token=TOKEN Enable Telegram remote control for daemon mode
|
|
5907
6210
|
--project-path=PATH Optional path to bootstrap as the default Projects root in daemon mode
|
|
6211
|
+
|
|
6212
|
+
Note: profile autoroll requires Pro.
|
|
5908
6213
|
`);
|
|
5909
6214
|
}
|
|
5910
6215
|
|
|
5911
6216
|
// src/platform/args.ts
|
|
5912
6217
|
var DEFAULT_AUTOROLL_INTERVAL_SECONDS = 30;
|
|
5913
|
-
var DEFAULT_AUTOROLL_THRESHOLD = 95;
|
|
5914
6218
|
function hasFlag(args, flag) {
|
|
5915
6219
|
return args.includes(flag);
|
|
5916
6220
|
}
|
|
@@ -7694,6 +7998,411 @@ async function assertProfileCreationAllowed(profileManager) {
|
|
|
7694
7998
|
}
|
|
7695
7999
|
}
|
|
7696
8000
|
|
|
8001
|
+
// ../../packages/runtime-codex/src/codex/officialAppRestart.ts
|
|
8002
|
+
var import_node_child_process7 = require("child_process");
|
|
8003
|
+
var import_node_fs8 = require("fs");
|
|
8004
|
+
var import_node_os5 = require("os");
|
|
8005
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
8006
|
+
var RESTART_COOLDOWN_MS = 6e4;
|
|
8007
|
+
var COMMAND_TIMEOUT_MS = 3e3;
|
|
8008
|
+
var EXIT_WAIT_MS = 1e4;
|
|
8009
|
+
var LAUNCH_WAIT_MS = 15e3;
|
|
8010
|
+
var POLL_INTERVAL_MS = 250;
|
|
8011
|
+
var APP_DISCOVERY_CACHE_TTL_MS = 6e4;
|
|
8012
|
+
var APP_DISCOVERY_MISS_CACHE_TTL_MS = 5e3;
|
|
8013
|
+
var restartPromise = null;
|
|
8014
|
+
var lastRestartStartedAt = 0;
|
|
8015
|
+
var profileActionLock = Promise.resolve();
|
|
8016
|
+
var appDiscoveryCache = null;
|
|
8017
|
+
function logOfficialCodexRestart(level, message, context) {
|
|
8018
|
+
const logger = level === "warn" ? console.warn : level === "error" ? console.error : console.info;
|
|
8019
|
+
if (context && Object.keys(context).length > 0) {
|
|
8020
|
+
logger(`[official-codex-restart] ${message}`, context);
|
|
8021
|
+
return;
|
|
8022
|
+
}
|
|
8023
|
+
logger(`[official-codex-restart] ${message}`);
|
|
8024
|
+
}
|
|
8025
|
+
function runCommand2(command, args, timeoutMs = COMMAND_TIMEOUT_MS) {
|
|
8026
|
+
return new Promise((resolve) => {
|
|
8027
|
+
const child = (0, import_node_child_process7.spawn)(command, args, {
|
|
8028
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
8029
|
+
});
|
|
8030
|
+
let stdout = "";
|
|
8031
|
+
let stderr = "";
|
|
8032
|
+
let timedOut = false;
|
|
8033
|
+
const timer = setTimeout(() => {
|
|
8034
|
+
timedOut = true;
|
|
8035
|
+
child.kill("SIGTERM");
|
|
8036
|
+
}, timeoutMs);
|
|
8037
|
+
child.stdout?.on("data", (chunk) => {
|
|
8038
|
+
stdout += String(chunk);
|
|
8039
|
+
});
|
|
8040
|
+
child.stderr?.on("data", (chunk) => {
|
|
8041
|
+
stderr += String(chunk);
|
|
8042
|
+
});
|
|
8043
|
+
child.on("error", (error) => {
|
|
8044
|
+
clearTimeout(timer);
|
|
8045
|
+
resolve({
|
|
8046
|
+
code: -1,
|
|
8047
|
+
stdout,
|
|
8048
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
8049
|
+
timedOut
|
|
8050
|
+
});
|
|
8051
|
+
});
|
|
8052
|
+
child.on("close", (code) => {
|
|
8053
|
+
clearTimeout(timer);
|
|
8054
|
+
resolve({ code, stdout, stderr, timedOut });
|
|
8055
|
+
});
|
|
8056
|
+
});
|
|
8057
|
+
}
|
|
8058
|
+
async function readBundleString(appPath, key) {
|
|
8059
|
+
const plistPath = import_node_path11.default.join(appPath, "Contents", "Info.plist");
|
|
8060
|
+
if (!(0, import_node_fs8.existsSync)(plistPath)) {
|
|
8061
|
+
return null;
|
|
8062
|
+
}
|
|
8063
|
+
const result = await runCommand2("/usr/bin/plutil", [
|
|
8064
|
+
"-extract",
|
|
8065
|
+
key,
|
|
8066
|
+
"raw",
|
|
8067
|
+
"-o",
|
|
8068
|
+
"-",
|
|
8069
|
+
plistPath
|
|
8070
|
+
]);
|
|
8071
|
+
if (result.code !== 0) {
|
|
8072
|
+
return null;
|
|
8073
|
+
}
|
|
8074
|
+
const bundleId = result.stdout.trim();
|
|
8075
|
+
return bundleId.length > 0 ? bundleId : null;
|
|
8076
|
+
}
|
|
8077
|
+
async function discoverCodexAppCandidates() {
|
|
8078
|
+
const now = Date.now();
|
|
8079
|
+
const envPath = process.env.CODEXUSE_OFFICIAL_CODEX_APP_PATH?.trim() || null;
|
|
8080
|
+
if (appDiscoveryCache && appDiscoveryCache.envPath === envPath && appDiscoveryCache.expiresAt > now) {
|
|
8081
|
+
return appDiscoveryCache.candidates;
|
|
8082
|
+
}
|
|
8083
|
+
const rawPaths = /* @__PURE__ */ new Set();
|
|
8084
|
+
if (envPath) {
|
|
8085
|
+
rawPaths.add(envPath);
|
|
8086
|
+
}
|
|
8087
|
+
rawPaths.add("/Applications/Codex.app");
|
|
8088
|
+
rawPaths.add(import_node_path11.default.join((0, import_node_os5.homedir)(), "Applications", "Codex.app"));
|
|
8089
|
+
const mdfind = await runCommand2(
|
|
8090
|
+
"/usr/bin/mdfind",
|
|
8091
|
+
["kMDItemFSName == 'Codex.app'"],
|
|
8092
|
+
1500
|
|
8093
|
+
);
|
|
8094
|
+
if (mdfind.code === 0) {
|
|
8095
|
+
for (const line of mdfind.stdout.split(/\r?\n/)) {
|
|
8096
|
+
const trimmed = line.trim();
|
|
8097
|
+
if (trimmed.endsWith("/Codex.app")) {
|
|
8098
|
+
rawPaths.add(trimmed);
|
|
8099
|
+
}
|
|
8100
|
+
}
|
|
8101
|
+
}
|
|
8102
|
+
const candidates = [];
|
|
8103
|
+
for (const appPath of rawPaths) {
|
|
8104
|
+
if (!(0, import_node_fs8.existsSync)(appPath) || import_node_path11.default.basename(appPath) !== "Codex.app") {
|
|
8105
|
+
continue;
|
|
8106
|
+
}
|
|
8107
|
+
const bundleId = await readBundleString(appPath, "CFBundleIdentifier");
|
|
8108
|
+
if (!bundleId || bundleId.toLowerCase().includes("codexuse")) {
|
|
8109
|
+
continue;
|
|
8110
|
+
}
|
|
8111
|
+
candidates.push({
|
|
8112
|
+
appPath,
|
|
8113
|
+
bundleId,
|
|
8114
|
+
version: await readBundleString(appPath, "CFBundleShortVersionString"),
|
|
8115
|
+
executableName: await readBundleString(appPath, "CFBundleExecutable") ?? "Codex"
|
|
8116
|
+
});
|
|
8117
|
+
}
|
|
8118
|
+
const sorted = candidates.sort((a, b) => a.appPath.localeCompare(b.appPath));
|
|
8119
|
+
appDiscoveryCache = {
|
|
8120
|
+
envPath,
|
|
8121
|
+
expiresAt: now + (sorted.length > 0 ? APP_DISCOVERY_CACHE_TTL_MS : APP_DISCOVERY_MISS_CACHE_TTL_MS),
|
|
8122
|
+
candidates: sorted
|
|
8123
|
+
};
|
|
8124
|
+
return sorted;
|
|
8125
|
+
}
|
|
8126
|
+
async function getRunningCodexPids(candidate) {
|
|
8127
|
+
const result = await runCommand2("/bin/ps", ["-axo", "pid=,args="], 2e3);
|
|
8128
|
+
const pids = /* @__PURE__ */ new Set();
|
|
8129
|
+
const appContentsRoot = `${import_node_path11.default.join(candidate.appPath, "Contents")}${import_node_path11.default.sep}`;
|
|
8130
|
+
if (result.code === 0) {
|
|
8131
|
+
for (const line of result.stdout.split(/\r?\n/)) {
|
|
8132
|
+
const match = line.match(/^\s*(\d+)\s+(.+)$/);
|
|
8133
|
+
if (!match) {
|
|
8134
|
+
continue;
|
|
8135
|
+
}
|
|
8136
|
+
const pid = Number(match[1]);
|
|
8137
|
+
const args = match[2] ?? "";
|
|
8138
|
+
if (Number.isInteger(pid) && args.includes(appContentsRoot)) {
|
|
8139
|
+
pids.add(pid);
|
|
8140
|
+
}
|
|
8141
|
+
}
|
|
8142
|
+
}
|
|
8143
|
+
return Array.from(pids).sort((a, b) => a - b);
|
|
8144
|
+
}
|
|
8145
|
+
async function getRunningCodexMainPids(candidate) {
|
|
8146
|
+
const result = await runCommand2("/bin/ps", ["-axo", "pid=,args="], 2e3);
|
|
8147
|
+
const pids = /* @__PURE__ */ new Set();
|
|
8148
|
+
const executablePath = import_node_path11.default.join(
|
|
8149
|
+
candidate.appPath,
|
|
8150
|
+
"Contents",
|
|
8151
|
+
"MacOS",
|
|
8152
|
+
candidate.executableName
|
|
8153
|
+
);
|
|
8154
|
+
if (result.code === 0) {
|
|
8155
|
+
for (const line of result.stdout.split(/\r?\n/)) {
|
|
8156
|
+
const match = line.match(/^\s*(\d+)\s+(.+)$/);
|
|
8157
|
+
if (!match) {
|
|
8158
|
+
continue;
|
|
8159
|
+
}
|
|
8160
|
+
const pid = Number(match[1]);
|
|
8161
|
+
const args = match[2] ?? "";
|
|
8162
|
+
if (Number.isInteger(pid) && args.includes(executablePath)) {
|
|
8163
|
+
pids.add(pid);
|
|
8164
|
+
}
|
|
8165
|
+
}
|
|
8166
|
+
}
|
|
8167
|
+
return Array.from(pids).sort((a, b) => a - b);
|
|
8168
|
+
}
|
|
8169
|
+
async function resolveCodexAppTarget() {
|
|
8170
|
+
const candidates = await discoverCodexAppCandidates();
|
|
8171
|
+
const fallback = candidates[0] ?? null;
|
|
8172
|
+
if (!fallback) {
|
|
8173
|
+
return { candidate: null, runningPids: [], mainPids: [] };
|
|
8174
|
+
}
|
|
8175
|
+
for (const candidate of candidates) {
|
|
8176
|
+
const [runningPids2, mainPids2] = await Promise.all([
|
|
8177
|
+
getRunningCodexPids(candidate),
|
|
8178
|
+
getRunningCodexMainPids(candidate)
|
|
8179
|
+
]);
|
|
8180
|
+
if (mainPids2.length > 0) {
|
|
8181
|
+
return { candidate, runningPids: runningPids2, mainPids: mainPids2 };
|
|
8182
|
+
}
|
|
8183
|
+
}
|
|
8184
|
+
const [runningPids, mainPids] = await Promise.all([
|
|
8185
|
+
getRunningCodexPids(fallback),
|
|
8186
|
+
getRunningCodexMainPids(fallback)
|
|
8187
|
+
]);
|
|
8188
|
+
return { candidate: fallback, runningPids, mainPids };
|
|
8189
|
+
}
|
|
8190
|
+
async function waitForCodexExit(candidate, timeoutMs) {
|
|
8191
|
+
const deadline = Date.now() + timeoutMs;
|
|
8192
|
+
while (Date.now() < deadline) {
|
|
8193
|
+
const pids = await getRunningCodexPids(candidate);
|
|
8194
|
+
if (pids.length === 0) {
|
|
8195
|
+
return true;
|
|
8196
|
+
}
|
|
8197
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
8198
|
+
}
|
|
8199
|
+
return false;
|
|
8200
|
+
}
|
|
8201
|
+
async function signalPids(pids, signal) {
|
|
8202
|
+
const signaled = [];
|
|
8203
|
+
for (const pid of pids) {
|
|
8204
|
+
try {
|
|
8205
|
+
process.kill(pid, signal);
|
|
8206
|
+
signaled.push(pid);
|
|
8207
|
+
} catch {
|
|
8208
|
+
}
|
|
8209
|
+
}
|
|
8210
|
+
return signaled;
|
|
8211
|
+
}
|
|
8212
|
+
async function openCodex(candidate) {
|
|
8213
|
+
const byBundle = await runCommand2("/usr/bin/open", ["-b", candidate.bundleId]);
|
|
8214
|
+
if (byBundle.code === 0) {
|
|
8215
|
+
return true;
|
|
8216
|
+
}
|
|
8217
|
+
const byPath = await runCommand2("/usr/bin/open", [candidate.appPath]);
|
|
8218
|
+
return byPath.code === 0;
|
|
8219
|
+
}
|
|
8220
|
+
async function waitForCodexLaunch(candidate) {
|
|
8221
|
+
const deadline = Date.now() + LAUNCH_WAIT_MS;
|
|
8222
|
+
while (Date.now() < deadline) {
|
|
8223
|
+
const pids = await getRunningCodexMainPids(candidate);
|
|
8224
|
+
const pid = pids[0] ?? null;
|
|
8225
|
+
if (pid !== null) {
|
|
8226
|
+
return pid;
|
|
8227
|
+
}
|
|
8228
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
8229
|
+
}
|
|
8230
|
+
return null;
|
|
8231
|
+
}
|
|
8232
|
+
async function rememberProfileSwitch(profileKey) {
|
|
8233
|
+
await patchAppState({
|
|
8234
|
+
officialCodex: {
|
|
8235
|
+
lastProfileSwitchAt: Date.now(),
|
|
8236
|
+
lastProfileSwitchProfileKey: profileKey
|
|
8237
|
+
}
|
|
8238
|
+
});
|
|
8239
|
+
}
|
|
8240
|
+
async function rememberRestartResult(args) {
|
|
8241
|
+
const now = Date.now();
|
|
8242
|
+
const verified = args.status === "restarted" || args.status === "started";
|
|
8243
|
+
await patchAppState({
|
|
8244
|
+
officialCodex: {
|
|
8245
|
+
lastVerifiedLaunchAt: verified ? now : void 0,
|
|
8246
|
+
lastVerifiedLaunchProfileKey: verified ? args.profileKey ?? null : void 0,
|
|
8247
|
+
lastObservedPid: verified ? args.pid ?? null : void 0,
|
|
8248
|
+
lastRestartStatus: args.status,
|
|
8249
|
+
lastRestartReason: args.reason ?? null
|
|
8250
|
+
}
|
|
8251
|
+
});
|
|
8252
|
+
}
|
|
8253
|
+
async function restartOfficialCodexAppOnce(options) {
|
|
8254
|
+
if (process.platform !== "darwin") {
|
|
8255
|
+
await rememberRestartResult({ status: "skipped", reason: "unsupported-platform" });
|
|
8256
|
+
return { status: "skipped", reason: "unsupported-platform" };
|
|
8257
|
+
}
|
|
8258
|
+
const now = Date.now();
|
|
8259
|
+
if (options.source !== "manual" && lastRestartStartedAt > 0 && now - lastRestartStartedAt < RESTART_COOLDOWN_MS) {
|
|
8260
|
+
await rememberRestartResult({
|
|
8261
|
+
status: "skipped",
|
|
8262
|
+
reason: "cooldown",
|
|
8263
|
+
profileKey: options.currentProfileKey
|
|
8264
|
+
});
|
|
8265
|
+
return { status: "skipped", reason: "cooldown" };
|
|
8266
|
+
}
|
|
8267
|
+
const { candidate, runningPids, mainPids } = await resolveCodexAppTarget();
|
|
8268
|
+
if (!candidate) {
|
|
8269
|
+
await rememberRestartResult({
|
|
8270
|
+
status: "skipped",
|
|
8271
|
+
reason: "official-codex-app-not-found",
|
|
8272
|
+
profileKey: options.currentProfileKey
|
|
8273
|
+
});
|
|
8274
|
+
return { status: "skipped", reason: "official-codex-app-not-found" };
|
|
8275
|
+
}
|
|
8276
|
+
const appIsRunning = mainPids.length > 0;
|
|
8277
|
+
if (!appIsRunning && !options.launchIfNotRunning) {
|
|
8278
|
+
await rememberRestartResult({
|
|
8279
|
+
status: "skipped",
|
|
8280
|
+
reason: "official-codex-app-not-running",
|
|
8281
|
+
profileKey: options.currentProfileKey
|
|
8282
|
+
});
|
|
8283
|
+
return { status: "skipped", reason: "official-codex-app-not-running" };
|
|
8284
|
+
}
|
|
8285
|
+
if (options.source !== "manual") {
|
|
8286
|
+
lastRestartStartedAt = Date.now();
|
|
8287
|
+
}
|
|
8288
|
+
if (appIsRunning) {
|
|
8289
|
+
logOfficialCodexRestart("warn", "Force killing official Codex before relaunch.", {
|
|
8290
|
+
appPath: candidate.appPath,
|
|
8291
|
+
bundleId: candidate.bundleId,
|
|
8292
|
+
source: options.source,
|
|
8293
|
+
runningPids,
|
|
8294
|
+
mainPids
|
|
8295
|
+
});
|
|
8296
|
+
const killedPids = await signalPids(runningPids, "SIGKILL");
|
|
8297
|
+
logOfficialCodexRestart("warn", "Sent SIGKILL to official Codex pids.", {
|
|
8298
|
+
appPath: candidate.appPath,
|
|
8299
|
+
bundleId: candidate.bundleId,
|
|
8300
|
+
source: options.source,
|
|
8301
|
+
killedPids
|
|
8302
|
+
});
|
|
8303
|
+
const exited = await waitForCodexExit(candidate, EXIT_WAIT_MS);
|
|
8304
|
+
if (!exited) {
|
|
8305
|
+
const remainingPids = await getRunningCodexPids(candidate);
|
|
8306
|
+
logOfficialCodexRestart("error", "Official Codex pids remained after force kill.", {
|
|
8307
|
+
appPath: candidate.appPath,
|
|
8308
|
+
bundleId: candidate.bundleId,
|
|
8309
|
+
source: options.source,
|
|
8310
|
+
remainingPids
|
|
8311
|
+
});
|
|
8312
|
+
const failed = {
|
|
8313
|
+
status: "failed",
|
|
8314
|
+
appPath: candidate.appPath,
|
|
8315
|
+
bundleId: candidate.bundleId,
|
|
8316
|
+
reason: "force-quit-timeout",
|
|
8317
|
+
phase: "quit"
|
|
8318
|
+
};
|
|
8319
|
+
await rememberRestartResult({
|
|
8320
|
+
status: failed.status,
|
|
8321
|
+
reason: failed.reason,
|
|
8322
|
+
profileKey: options.currentProfileKey
|
|
8323
|
+
});
|
|
8324
|
+
return failed;
|
|
8325
|
+
}
|
|
8326
|
+
logOfficialCodexRestart("info", "Official Codex exited after force kill.", {
|
|
8327
|
+
appPath: candidate.appPath,
|
|
8328
|
+
bundleId: candidate.bundleId,
|
|
8329
|
+
source: options.source
|
|
8330
|
+
});
|
|
8331
|
+
}
|
|
8332
|
+
const opened = await openCodex(candidate);
|
|
8333
|
+
if (!opened) {
|
|
8334
|
+
const failed = {
|
|
8335
|
+
status: "failed",
|
|
8336
|
+
appPath: candidate.appPath,
|
|
8337
|
+
bundleId: candidate.bundleId,
|
|
8338
|
+
reason: "open-failed",
|
|
8339
|
+
phase: "launch"
|
|
8340
|
+
};
|
|
8341
|
+
await rememberRestartResult({
|
|
8342
|
+
status: failed.status,
|
|
8343
|
+
reason: failed.reason,
|
|
8344
|
+
profileKey: options.currentProfileKey
|
|
8345
|
+
});
|
|
8346
|
+
return failed;
|
|
8347
|
+
}
|
|
8348
|
+
const launchedPid = await waitForCodexLaunch(candidate);
|
|
8349
|
+
if (launchedPid === null) {
|
|
8350
|
+
logOfficialCodexRestart("error", "Official Codex launch was not verified.", {
|
|
8351
|
+
appPath: candidate.appPath,
|
|
8352
|
+
bundleId: candidate.bundleId,
|
|
8353
|
+
source: options.source
|
|
8354
|
+
});
|
|
8355
|
+
const failed = {
|
|
8356
|
+
status: "failed",
|
|
8357
|
+
appPath: candidate.appPath,
|
|
8358
|
+
bundleId: candidate.bundleId,
|
|
8359
|
+
reason: "launch-timeout",
|
|
8360
|
+
phase: "launch"
|
|
8361
|
+
};
|
|
8362
|
+
await rememberRestartResult({
|
|
8363
|
+
status: failed.status,
|
|
8364
|
+
reason: failed.reason,
|
|
8365
|
+
profileKey: options.currentProfileKey
|
|
8366
|
+
});
|
|
8367
|
+
return failed;
|
|
8368
|
+
}
|
|
8369
|
+
const status = appIsRunning ? "restarted" : "started";
|
|
8370
|
+
logOfficialCodexRestart("info", "Official Codex launch verified.", {
|
|
8371
|
+
appPath: candidate.appPath,
|
|
8372
|
+
bundleId: candidate.bundleId,
|
|
8373
|
+
source: options.source,
|
|
8374
|
+
status,
|
|
8375
|
+
pid: launchedPid
|
|
8376
|
+
});
|
|
8377
|
+
await rememberRestartResult({
|
|
8378
|
+
status,
|
|
8379
|
+
profileKey: options.currentProfileKey,
|
|
8380
|
+
pid: launchedPid
|
|
8381
|
+
});
|
|
8382
|
+
return {
|
|
8383
|
+
status,
|
|
8384
|
+
appPath: candidate.appPath,
|
|
8385
|
+
bundleId: candidate.bundleId,
|
|
8386
|
+
pid: launchedPid
|
|
8387
|
+
};
|
|
8388
|
+
}
|
|
8389
|
+
function recordOfficialCodexProfileSwitch(profileKey) {
|
|
8390
|
+
return rememberProfileSwitch(profileKey);
|
|
8391
|
+
}
|
|
8392
|
+
function restartOfficialCodexApp(options = {}) {
|
|
8393
|
+
if (restartPromise) {
|
|
8394
|
+
return restartPromise;
|
|
8395
|
+
}
|
|
8396
|
+
restartPromise = restartOfficialCodexAppOnce({
|
|
8397
|
+
source: options.source ?? "manual",
|
|
8398
|
+
currentProfileKey: options.currentProfileKey ?? null,
|
|
8399
|
+
launchIfNotRunning: options.launchIfNotRunning ?? (options.source ?? "manual") === "manual"
|
|
8400
|
+
}).finally(() => {
|
|
8401
|
+
restartPromise = null;
|
|
8402
|
+
});
|
|
8403
|
+
return restartPromise;
|
|
8404
|
+
}
|
|
8405
|
+
|
|
7697
8406
|
// src/commands/profile.ts
|
|
7698
8407
|
function formatProfileLabel(name, displayName) {
|
|
7699
8408
|
if (displayName && displayName.trim() && displayName !== name) {
|
|
@@ -7812,6 +8521,7 @@ function formatWindowUsage(window) {
|
|
|
7812
8521
|
var EMPTY_USAGE_SUMMARY = {
|
|
7813
8522
|
windowKey: "primary",
|
|
7814
8523
|
usagePercent: 0,
|
|
8524
|
+
remainingPercent: 100,
|
|
7815
8525
|
hasUsage: false,
|
|
7816
8526
|
resetsInSeconds: null,
|
|
7817
8527
|
windowMinutes: null
|
|
@@ -7835,6 +8545,7 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
7835
8545
|
}
|
|
7836
8546
|
const usageValueRaw = toFiniteNumberOrNull(entry.window.usedPercent);
|
|
7837
8547
|
const usageValue = usageValueRaw === null ? 0 : Math.max(0, Math.min(100, usageValueRaw));
|
|
8548
|
+
const remainingValue = 100 - usageValue;
|
|
7838
8549
|
const hasUsage = usageValueRaw !== null;
|
|
7839
8550
|
const resetsInSeconds = toFiniteNumberOrNull(entry.window.resetsInSeconds);
|
|
7840
8551
|
const windowMinutes = toFiniteNumberOrNull(entry.window.windowMinutes);
|
|
@@ -7843,6 +8554,7 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
7843
8554
|
summary = {
|
|
7844
8555
|
windowKey: entry.key,
|
|
7845
8556
|
usagePercent: usageValue,
|
|
8557
|
+
remainingPercent: remainingValue,
|
|
7846
8558
|
hasUsage,
|
|
7847
8559
|
resetsInSeconds,
|
|
7848
8560
|
windowMinutes
|
|
@@ -7853,6 +8565,7 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
7853
8565
|
summary = {
|
|
7854
8566
|
windowKey: entry.key,
|
|
7855
8567
|
usagePercent: usageValue,
|
|
8568
|
+
remainingPercent: remainingValue,
|
|
7856
8569
|
hasUsage,
|
|
7857
8570
|
resetsInSeconds,
|
|
7858
8571
|
windowMinutes
|
|
@@ -7864,6 +8577,7 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
7864
8577
|
summary = {
|
|
7865
8578
|
windowKey: entry.key,
|
|
7866
8579
|
usagePercent: usageValue,
|
|
8580
|
+
remainingPercent: remainingValue,
|
|
7867
8581
|
hasUsage,
|
|
7868
8582
|
resetsInSeconds,
|
|
7869
8583
|
windowMinutes
|
|
@@ -7875,6 +8589,7 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
7875
8589
|
summary = {
|
|
7876
8590
|
windowKey: entry.key,
|
|
7877
8591
|
usagePercent: usageValue,
|
|
8592
|
+
remainingPercent: remainingValue,
|
|
7878
8593
|
hasUsage,
|
|
7879
8594
|
resetsInSeconds,
|
|
7880
8595
|
windowMinutes
|
|
@@ -7885,6 +8600,7 @@ function summarizeRateLimitSnapshot(snapshot) {
|
|
|
7885
8600
|
summary = {
|
|
7886
8601
|
windowKey: entry.key,
|
|
7887
8602
|
usagePercent: usageValue,
|
|
8603
|
+
remainingPercent: remainingValue,
|
|
7888
8604
|
hasUsage,
|
|
7889
8605
|
resetsInSeconds,
|
|
7890
8606
|
windowMinutes
|
|
@@ -7929,9 +8645,16 @@ async function resolveProfileUsage(manager, profile, codexPath) {
|
|
|
7929
8645
|
return fallbackUsage(profile);
|
|
7930
8646
|
}
|
|
7931
8647
|
}
|
|
7932
|
-
function compareAutoRollCandidates(a, b) {
|
|
7933
|
-
|
|
7934
|
-
|
|
8648
|
+
function compareAutoRollCandidates(a, b, priorityRanks) {
|
|
8649
|
+
const aRank = priorityRanks.get(resolveProfileIdentityKeyFromProfile(a.profile));
|
|
8650
|
+
const bRank = priorityRanks.get(resolveProfileIdentityKeyFromProfile(b.profile));
|
|
8651
|
+
if (aRank !== void 0 || bRank !== void 0) {
|
|
8652
|
+
if (aRank === void 0) return 1;
|
|
8653
|
+
if (bRank === void 0) return -1;
|
|
8654
|
+
if (aRank !== bRank) return aRank - bRank;
|
|
8655
|
+
}
|
|
8656
|
+
if (a.usageSummary.remainingPercent !== b.usageSummary.remainingPercent) {
|
|
8657
|
+
return b.usageSummary.remainingPercent - a.usageSummary.remainingPercent;
|
|
7935
8658
|
}
|
|
7936
8659
|
const aReset = a.usageSummary.resetsInSeconds ?? Number.POSITIVE_INFINITY;
|
|
7937
8660
|
const bReset = b.usageSummary.resetsInSeconds ?? Number.POSITIVE_INFINITY;
|
|
@@ -7940,37 +8663,38 @@ function compareAutoRollCandidates(a, b) {
|
|
|
7940
8663
|
}
|
|
7941
8664
|
return a.profile.name.localeCompare(b.profile.name);
|
|
7942
8665
|
}
|
|
7943
|
-
function computeAutoRollCandidate(profiles, usageMap, currentProfileName, currentSummary,
|
|
7944
|
-
const
|
|
8666
|
+
function computeAutoRollCandidate(profiles, usageMap, currentProfileName, currentSummary, switchRemainingThreshold, priorityOrder = [], blockedProfileNames = /* @__PURE__ */ new Set()) {
|
|
8667
|
+
const priorityRanks = /* @__PURE__ */ new Map();
|
|
8668
|
+
for (const [index, key] of priorityOrder.entries()) {
|
|
8669
|
+
const normalized = key.trim();
|
|
8670
|
+
if (normalized && !priorityRanks.has(normalized)) {
|
|
8671
|
+
priorityRanks.set(normalized, index);
|
|
8672
|
+
}
|
|
8673
|
+
}
|
|
8674
|
+
const candidates = profiles.filter(
|
|
8675
|
+
(profile) => profile.name !== currentProfileName && !blockedProfileNames.has(profile.name) && profile.isValid && !profile.tokenStatus?.requiresUserAction
|
|
8676
|
+
).map((profile) => ({
|
|
7945
8677
|
profile,
|
|
7946
8678
|
usageSummary: usageMap.get(profile.name)?.usageSummary ?? summarizeRateLimitSnapshot(profile.rateLimit ?? null)
|
|
7947
8679
|
}));
|
|
7948
8680
|
if (candidates.length === 0) {
|
|
7949
8681
|
return null;
|
|
7950
8682
|
}
|
|
7951
|
-
const belowThreshold = candidates.filter(
|
|
8683
|
+
const belowThreshold = candidates.filter(
|
|
8684
|
+
(candidate) => candidate.usageSummary.hasUsage && candidate.usageSummary.remainingPercent > switchRemainingThreshold
|
|
8685
|
+
).sort((a, b) => compareAutoRollCandidates(a, b, priorityRanks));
|
|
7952
8686
|
let selected = belowThreshold[0] ?? null;
|
|
7953
8687
|
if (!selected) {
|
|
7954
|
-
selected = candidates.filter((candidate) => candidate.usageSummary.hasUsage).sort(compareAutoRollCandidates)[0] ?? null;
|
|
7955
|
-
}
|
|
7956
|
-
if (!selected) {
|
|
7957
|
-
selected = candidates.filter((candidate) => !candidate.usageSummary.hasUsage).sort((a, b) => {
|
|
7958
|
-
const aReset = a.usageSummary.resetsInSeconds ?? Number.POSITIVE_INFINITY;
|
|
7959
|
-
const bReset = b.usageSummary.resetsInSeconds ?? Number.POSITIVE_INFINITY;
|
|
7960
|
-
if (aReset !== bReset) {
|
|
7961
|
-
return aReset - bReset;
|
|
7962
|
-
}
|
|
7963
|
-
return a.profile.name.localeCompare(b.profile.name);
|
|
7964
|
-
})[0] ?? null;
|
|
8688
|
+
selected = candidates.filter((candidate) => !candidate.usageSummary.hasUsage).sort((a, b) => compareAutoRollCandidates(a, b, priorityRanks))[0] ?? null;
|
|
7965
8689
|
}
|
|
7966
8690
|
if (!selected) {
|
|
7967
8691
|
return null;
|
|
7968
8692
|
}
|
|
7969
8693
|
if (selected.usageSummary.hasUsage && currentSummary.hasUsage) {
|
|
7970
|
-
if (selected.usageSummary.
|
|
8694
|
+
if (selected.usageSummary.remainingPercent < currentSummary.remainingPercent) {
|
|
7971
8695
|
return null;
|
|
7972
8696
|
}
|
|
7973
|
-
if (selected.usageSummary.
|
|
8697
|
+
if (selected.usageSummary.remainingPercent === currentSummary.remainingPercent) {
|
|
7974
8698
|
const selectedReset = selected.usageSummary.resetsInSeconds ?? Number.POSITIVE_INFINITY;
|
|
7975
8699
|
const currentReset = currentSummary.resetsInSeconds ?? Number.POSITIVE_INFINITY;
|
|
7976
8700
|
if (selectedReset >= currentReset) {
|
|
@@ -8054,18 +8778,31 @@ async function mapWithConcurrency(items, limit, fn) {
|
|
|
8054
8778
|
await Promise.all(workers);
|
|
8055
8779
|
return results;
|
|
8056
8780
|
}
|
|
8057
|
-
function
|
|
8058
|
-
const
|
|
8059
|
-
|
|
8060
|
-
|
|
8781
|
+
function parseAutoRollSwitchRemainingThreshold(flags, fallbackThreshold) {
|
|
8782
|
+
const explicitLeft = parseNumericFlag(flags, "--switch-left");
|
|
8783
|
+
const explicitLegacyUsed = parseNumericFlag(flags, "--threshold");
|
|
8784
|
+
if (explicitLeft !== null && explicitLegacyUsed !== null) {
|
|
8785
|
+
throw new Error("Use either --switch-left or legacy --threshold, not both.");
|
|
8061
8786
|
}
|
|
8062
|
-
if (
|
|
8063
|
-
|
|
8787
|
+
if (explicitLeft !== null) {
|
|
8788
|
+
if (Number.isNaN(explicitLeft)) {
|
|
8789
|
+
throw new Error("Invalid --switch-left value. Use --switch-left=0-50.");
|
|
8790
|
+
}
|
|
8791
|
+
if (explicitLeft < AUTO_ROLL_SWITCH_REMAINING_MIN || explicitLeft > AUTO_ROLL_SWITCH_REMAINING_MAX) {
|
|
8792
|
+
throw new Error("Invalid --switch-left value. Use a number between 0 and 50.");
|
|
8793
|
+
}
|
|
8794
|
+
return sanitizeAutoRollSwitchRemainingThreshold(explicitLeft);
|
|
8064
8795
|
}
|
|
8065
|
-
if (
|
|
8066
|
-
|
|
8796
|
+
if (explicitLegacyUsed !== null) {
|
|
8797
|
+
if (Number.isNaN(explicitLegacyUsed)) {
|
|
8798
|
+
throw new Error("Invalid --threshold value. Use --threshold=50-100.");
|
|
8799
|
+
}
|
|
8800
|
+
if (explicitLegacyUsed < 50 || explicitLegacyUsed > 100) {
|
|
8801
|
+
throw new Error("Invalid --threshold value. Use a number between 50 and 100.");
|
|
8802
|
+
}
|
|
8803
|
+
return sanitizeAutoRollSwitchRemainingThreshold(100 - explicitLegacyUsed);
|
|
8067
8804
|
}
|
|
8068
|
-
return
|
|
8805
|
+
return sanitizeAutoRollSwitchRemainingThreshold(fallbackThreshold);
|
|
8069
8806
|
}
|
|
8070
8807
|
function parseAutoRollIntervalSeconds(flags) {
|
|
8071
8808
|
const explicitInterval = parseIntegerFlag(flags, "--interval");
|
|
@@ -8077,6 +8814,74 @@ function parseAutoRollIntervalSeconds(flags) {
|
|
|
8077
8814
|
}
|
|
8078
8815
|
return explicitInterval;
|
|
8079
8816
|
}
|
|
8817
|
+
function resolveRestartCodexFlag(flags, fallback) {
|
|
8818
|
+
const restart = hasFlag(flags, "--restart-codex");
|
|
8819
|
+
const noRestart = hasFlag(flags, "--no-restart-codex");
|
|
8820
|
+
if (restart && noRestart) {
|
|
8821
|
+
throw new Error("Use either --restart-codex or --no-restart-codex, not both.");
|
|
8822
|
+
}
|
|
8823
|
+
if (restart) {
|
|
8824
|
+
return true;
|
|
8825
|
+
}
|
|
8826
|
+
if (noRestart) {
|
|
8827
|
+
return false;
|
|
8828
|
+
}
|
|
8829
|
+
return fallback;
|
|
8830
|
+
}
|
|
8831
|
+
function findProfileKey(profiles, name) {
|
|
8832
|
+
const profile = profiles.find((entry) => entry.name === name);
|
|
8833
|
+
return profile ? profileRateLimitKey(profile) : null;
|
|
8834
|
+
}
|
|
8835
|
+
function rearmBlockedProfiles(blockedProfiles, usageMap, rearmRemainingThreshold) {
|
|
8836
|
+
if (!blockedProfiles) {
|
|
8837
|
+
return;
|
|
8838
|
+
}
|
|
8839
|
+
for (const profileName of Array.from(blockedProfiles)) {
|
|
8840
|
+
const summary = usageMap.get(profileName)?.usageSummary;
|
|
8841
|
+
if (!summary || !summary.hasUsage || summary.remainingPercent > rearmRemainingThreshold) {
|
|
8842
|
+
blockedProfiles.delete(profileName);
|
|
8843
|
+
}
|
|
8844
|
+
}
|
|
8845
|
+
}
|
|
8846
|
+
async function rememberOfficialCodexSwitch(profileKey) {
|
|
8847
|
+
try {
|
|
8848
|
+
await recordOfficialCodexProfileSwitch(profileKey);
|
|
8849
|
+
} catch (error) {
|
|
8850
|
+
console.warn(
|
|
8851
|
+
`Could not record official Codex sync state: ${error instanceof Error ? error.message : String(error)}`
|
|
8852
|
+
);
|
|
8853
|
+
}
|
|
8854
|
+
}
|
|
8855
|
+
function formatOfficialCodexRestart(result) {
|
|
8856
|
+
if (result.status === "restarted") {
|
|
8857
|
+
return "Official Codex restarted.";
|
|
8858
|
+
}
|
|
8859
|
+
if (result.status === "started") {
|
|
8860
|
+
return "Official Codex started.";
|
|
8861
|
+
}
|
|
8862
|
+
if (result.status === "failed") {
|
|
8863
|
+
return `Official Codex restart failed: ${result.reason}.`;
|
|
8864
|
+
}
|
|
8865
|
+
if (result.status === "skipped") {
|
|
8866
|
+
return `Official Codex restart skipped: ${result.reason}.`;
|
|
8867
|
+
}
|
|
8868
|
+
return "Official Codex restart status unknown.";
|
|
8869
|
+
}
|
|
8870
|
+
async function maybeRestartOfficialCodex(options) {
|
|
8871
|
+
if (!options.enabled) {
|
|
8872
|
+
return null;
|
|
8873
|
+
}
|
|
8874
|
+
try {
|
|
8875
|
+
const result = await restartOfficialCodexApp({
|
|
8876
|
+
source: options.source,
|
|
8877
|
+
currentProfileKey: options.profileKey,
|
|
8878
|
+
launchIfNotRunning: options.launchIfNotRunning
|
|
8879
|
+
});
|
|
8880
|
+
return formatOfficialCodexRestart(result);
|
|
8881
|
+
} catch (error) {
|
|
8882
|
+
return `Official Codex restart failed: ${error instanceof Error ? error.message : String(error)}.`;
|
|
8883
|
+
}
|
|
8884
|
+
}
|
|
8080
8885
|
async function collectUsageMap(manager, profiles, codexPath) {
|
|
8081
8886
|
const usageMap = /* @__PURE__ */ new Map();
|
|
8082
8887
|
const uniqueProfilesByAccount = /* @__PURE__ */ new Map();
|
|
@@ -8126,6 +8931,11 @@ async function runAutoRollPass(manager, options) {
|
|
|
8126
8931
|
throw new Error("Codex CLI binary not found on PATH. Auto-roll requires codex for live rate limits.");
|
|
8127
8932
|
}
|
|
8128
8933
|
const usageMap = await collectUsageMap(manager, profiles, codexPath);
|
|
8934
|
+
rearmBlockedProfiles(
|
|
8935
|
+
options.blockedProfiles,
|
|
8936
|
+
usageMap,
|
|
8937
|
+
options.rearmRemainingThreshold
|
|
8938
|
+
);
|
|
8129
8939
|
const currentProfile = profiles.find((profile) => profile.name === current.name);
|
|
8130
8940
|
if (!currentProfile) {
|
|
8131
8941
|
return {
|
|
@@ -8144,10 +8954,31 @@ async function runAutoRollPass(manager, options) {
|
|
|
8144
8954
|
target: null
|
|
8145
8955
|
};
|
|
8146
8956
|
}
|
|
8147
|
-
if (
|
|
8957
|
+
if (options.blockedProfiles?.has(current.name)) {
|
|
8958
|
+
if (currentUsage.remainingPercent > options.rearmRemainingThreshold) {
|
|
8959
|
+
options.blockedProfiles.delete(current.name);
|
|
8960
|
+
} else {
|
|
8961
|
+
return {
|
|
8962
|
+
switched: false,
|
|
8963
|
+
message: `'${current.name}' at ${Math.round(currentUsage.remainingPercent)}% left; waiting for re-arm above ${options.rearmRemainingThreshold}% left.`,
|
|
8964
|
+
source: current.name,
|
|
8965
|
+
target: null
|
|
8966
|
+
};
|
|
8967
|
+
}
|
|
8968
|
+
}
|
|
8969
|
+
if (options.blockCurrentUntilRearm && currentUsage.remainingPercent <= options.switchRemainingThreshold) {
|
|
8970
|
+
options.blockedProfiles?.add(current.name);
|
|
8971
|
+
return {
|
|
8972
|
+
switched: false,
|
|
8973
|
+
message: `'${current.name}' at ${Math.round(currentUsage.remainingPercent)}% left; waiting for re-arm above ${options.rearmRemainingThreshold}% left.`,
|
|
8974
|
+
source: current.name,
|
|
8975
|
+
target: null
|
|
8976
|
+
};
|
|
8977
|
+
}
|
|
8978
|
+
if (currentUsage.remainingPercent > options.switchRemainingThreshold) {
|
|
8148
8979
|
return {
|
|
8149
8980
|
switched: false,
|
|
8150
|
-
message: `'${current.name}' at ${Math.round(currentUsage.
|
|
8981
|
+
message: `'${current.name}' at ${Math.round(currentUsage.remainingPercent)}% left, above switch threshold ${options.switchRemainingThreshold}% left.`,
|
|
8151
8982
|
source: current.name,
|
|
8152
8983
|
target: null
|
|
8153
8984
|
};
|
|
@@ -8157,29 +8988,41 @@ async function runAutoRollPass(manager, options) {
|
|
|
8157
8988
|
usageMap,
|
|
8158
8989
|
current.name,
|
|
8159
8990
|
currentUsage,
|
|
8160
|
-
options.
|
|
8991
|
+
options.switchRemainingThreshold,
|
|
8992
|
+
options.priorityOrder ?? [],
|
|
8993
|
+
options.blockedProfiles
|
|
8161
8994
|
);
|
|
8162
8995
|
if (!candidate) {
|
|
8163
8996
|
return {
|
|
8164
8997
|
switched: false,
|
|
8165
|
-
message: `No eligible profile to switch from '${current.name}' at ${Math.round(currentUsage.
|
|
8998
|
+
message: `No eligible profile to switch from '${current.name}' at ${Math.round(currentUsage.remainingPercent)}% left.`,
|
|
8166
8999
|
source: current.name,
|
|
8167
9000
|
target: null
|
|
8168
9001
|
};
|
|
8169
9002
|
}
|
|
8170
|
-
const candidateUsage = candidate.usageSummary.hasUsage ? `${Math.round(candidate.usageSummary.
|
|
9003
|
+
const candidateUsage = candidate.usageSummary.hasUsage ? `${Math.round(candidate.usageSummary.remainingPercent)}% left` : "n/a";
|
|
8171
9004
|
if (options.dryRun) {
|
|
9005
|
+
const restartSuffix = options.restartOfficialCodex ? " Official Codex would restart after the switch if running." : "";
|
|
8172
9006
|
return {
|
|
8173
9007
|
switched: false,
|
|
8174
|
-
message: `[dry-run] Would switch '${current.name}' (${Math.round(currentUsage.
|
|
9008
|
+
message: `[dry-run] Would switch '${current.name}' (${Math.round(currentUsage.remainingPercent)}% left) -> '${candidate.profile.name}' (${candidateUsage}).${restartSuffix}`,
|
|
8175
9009
|
source: current.name,
|
|
8176
9010
|
target: candidate.profile.name
|
|
8177
9011
|
};
|
|
8178
9012
|
}
|
|
8179
9013
|
await manager.switchToProfile(candidate.profile.name);
|
|
9014
|
+
const targetProfileKey = profileRateLimitKey(candidate.profile);
|
|
9015
|
+
await rememberOfficialCodexSwitch(targetProfileKey);
|
|
9016
|
+
const restartMessage = await maybeRestartOfficialCodex({
|
|
9017
|
+
enabled: options.restartOfficialCodex,
|
|
9018
|
+
source: "auto-roll",
|
|
9019
|
+
profileKey: targetProfileKey,
|
|
9020
|
+
launchIfNotRunning: options.launchOfficialCodexWhenClosed === true
|
|
9021
|
+
});
|
|
9022
|
+
options.blockedProfiles?.add(current.name);
|
|
8180
9023
|
return {
|
|
8181
9024
|
switched: true,
|
|
8182
|
-
message: `Auto-rolled '${current.name}' (${Math.round(currentUsage.
|
|
9025
|
+
message: `Auto-rolled '${current.name}' (${Math.round(currentUsage.remainingPercent)}% left) -> '${candidate.profile.name}' (${candidateUsage}).${restartMessage ? ` ${restartMessage}` : ""}`,
|
|
8183
9026
|
source: current.name,
|
|
8184
9027
|
target: candidate.profile.name
|
|
8185
9028
|
};
|
|
@@ -8279,20 +9122,51 @@ async function handleProfileCommand(args, version) {
|
|
|
8279
9122
|
if (!name) {
|
|
8280
9123
|
throw new Error("Profile name is required.");
|
|
8281
9124
|
}
|
|
9125
|
+
const restartOfficialCodex = resolveRestartCodexFlag(flags, false);
|
|
9126
|
+
const profiles = await manager.listProfiles();
|
|
9127
|
+
const targetProfileKey = findProfileKey(profiles, name);
|
|
8282
9128
|
await manager.switchToProfile(name);
|
|
8283
9129
|
console.log(`Switched to profile: ${name}`);
|
|
9130
|
+
await rememberOfficialCodexSwitch(targetProfileKey);
|
|
9131
|
+
const restartMessage = await maybeRestartOfficialCodex({
|
|
9132
|
+
enabled: restartOfficialCodex,
|
|
9133
|
+
source: "manual",
|
|
9134
|
+
profileKey: targetProfileKey
|
|
9135
|
+
});
|
|
9136
|
+
if (restartMessage) {
|
|
9137
|
+
console.log(restartMessage);
|
|
9138
|
+
}
|
|
8284
9139
|
return;
|
|
8285
9140
|
}
|
|
8286
9141
|
case "autoroll":
|
|
8287
9142
|
case "auto-roll": {
|
|
9143
|
+
const license = await licenseService.getStatus();
|
|
9144
|
+
if (!license.isPro) {
|
|
9145
|
+
throw new Error("Auto-roll requires a Pro license.");
|
|
9146
|
+
}
|
|
8288
9147
|
const watch = hasFlag(flags, "--watch");
|
|
8289
9148
|
const dryRun = hasFlag(flags, "--dry-run");
|
|
8290
9149
|
const settings = await getStoredAutoRollSettings().catch(() => null);
|
|
8291
|
-
const
|
|
8292
|
-
|
|
9150
|
+
const restartOfficialCodex = resolveRestartCodexFlag(
|
|
9151
|
+
flags,
|
|
9152
|
+
settings?.restartOfficialCodexOnAutoRoll === true
|
|
9153
|
+
);
|
|
9154
|
+
const fallbackSwitchRemainingThreshold = typeof settings?.switchRemainingThreshold === "number" && Number.isFinite(settings.switchRemainingThreshold) ? settings.switchRemainingThreshold : DEFAULT_AUTO_ROLL_SWITCH_REMAINING_THRESHOLD;
|
|
9155
|
+
const switchRemainingThreshold = parseAutoRollSwitchRemainingThreshold(
|
|
9156
|
+
flags,
|
|
9157
|
+
fallbackSwitchRemainingThreshold
|
|
9158
|
+
);
|
|
9159
|
+
const rearmRemainingThreshold = sanitizeAutoRollRearmRemainingThreshold(
|
|
9160
|
+
settings?.rearmRemainingThreshold ?? DEFAULT_AUTO_ROLL_REARM_REMAINING_THRESHOLD,
|
|
9161
|
+
switchRemainingThreshold
|
|
9162
|
+
);
|
|
8293
9163
|
const intervalSeconds = parseAutoRollIntervalSeconds(flags);
|
|
8294
9164
|
if (watch) {
|
|
8295
|
-
console.log(
|
|
9165
|
+
console.log(
|
|
9166
|
+
`Auto-roll watch: switch at/below ${switchRemainingThreshold}% left | re-arm above ${rearmRemainingThreshold}% left | interval ${intervalSeconds}s${dryRun ? " | dry-run" : ""}${restartOfficialCodex ? " | restart-codex" : ""}`
|
|
9167
|
+
);
|
|
9168
|
+
const blockedProfiles = /* @__PURE__ */ new Set();
|
|
9169
|
+
let lastObservedProfile = null;
|
|
8296
9170
|
let stop = false;
|
|
8297
9171
|
process.on("SIGINT", () => {
|
|
8298
9172
|
stop = true;
|
|
@@ -8301,8 +9175,23 @@ async function handleProfileCommand(args, version) {
|
|
|
8301
9175
|
stop = true;
|
|
8302
9176
|
});
|
|
8303
9177
|
while (!stop) {
|
|
8304
|
-
const
|
|
9178
|
+
const current = await manager.getCurrentProfile();
|
|
9179
|
+
const currentName = current.name ?? null;
|
|
9180
|
+
const blockCurrentUntilRearm = Boolean(
|
|
9181
|
+
currentName && lastObservedProfile && currentName !== lastObservedProfile
|
|
9182
|
+
);
|
|
9183
|
+
const result2 = await runAutoRollPass(manager, {
|
|
9184
|
+
switchRemainingThreshold,
|
|
9185
|
+
rearmRemainingThreshold,
|
|
9186
|
+
dryRun,
|
|
9187
|
+
priorityOrder: settings?.priorityOrder ?? [],
|
|
9188
|
+
restartOfficialCodex,
|
|
9189
|
+
launchOfficialCodexWhenClosed: settings?.launchOfficialCodexWhenClosedOnAutoRoll === true,
|
|
9190
|
+
blockedProfiles,
|
|
9191
|
+
blockCurrentUntilRearm
|
|
9192
|
+
});
|
|
8305
9193
|
console.log(result2.message);
|
|
9194
|
+
lastObservedProfile = result2.switched ? result2.target ?? currentName : currentName ?? result2.source;
|
|
8306
9195
|
if (stop) {
|
|
8307
9196
|
break;
|
|
8308
9197
|
}
|
|
@@ -8311,7 +9200,14 @@ async function handleProfileCommand(args, version) {
|
|
|
8311
9200
|
console.log("Auto-roll watch stopped.");
|
|
8312
9201
|
return;
|
|
8313
9202
|
}
|
|
8314
|
-
const result = await runAutoRollPass(manager, {
|
|
9203
|
+
const result = await runAutoRollPass(manager, {
|
|
9204
|
+
switchRemainingThreshold,
|
|
9205
|
+
rearmRemainingThreshold,
|
|
9206
|
+
dryRun,
|
|
9207
|
+
priorityOrder: settings?.priorityOrder ?? [],
|
|
9208
|
+
restartOfficialCodex,
|
|
9209
|
+
launchOfficialCodexWhenClosed: settings?.launchOfficialCodexWhenClosedOnAutoRoll === true
|
|
9210
|
+
});
|
|
8315
9211
|
console.log(result.message);
|
|
8316
9212
|
return;
|
|
8317
9213
|
}
|
|
@@ -8341,8 +9237,8 @@ async function handleProfileCommand(args, version) {
|
|
|
8341
9237
|
}
|
|
8342
9238
|
|
|
8343
9239
|
// ../../packages/runtime-profiles/src/cloud-sync/service.ts
|
|
8344
|
-
var
|
|
8345
|
-
var
|
|
9240
|
+
var import_node_fs9 = require("fs");
|
|
9241
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
8346
9242
|
|
|
8347
9243
|
// ../../packages/contracts/src/cloud-sync/types.ts
|
|
8348
9244
|
var CLOUD_SYNC_SCHEMA_VERSION = 1;
|
|
@@ -8502,9 +9398,9 @@ async function pushRemoteSnapshot(licenseKey, snapshot, options) {
|
|
|
8502
9398
|
var import_toml2 = __toESM(require_toml(), 1);
|
|
8503
9399
|
|
|
8504
9400
|
// ../../packages/runtime-codex/src/codex/config-metadata.ts
|
|
8505
|
-
var
|
|
9401
|
+
var import_node_child_process8 = require("child_process");
|
|
8506
9402
|
var import_promises = require("fs/promises");
|
|
8507
|
-
var
|
|
9403
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
8508
9404
|
var CONFIG_SCHEMA_URL = "https://raw.githubusercontent.com/openai/codex/main/codex-rs/core/config.schema.json";
|
|
8509
9405
|
var METADATA_CACHE_TTL_MS = 6e4;
|
|
8510
9406
|
var FALLBACK_SCHEMA_TOP_LEVEL_KEYS = [
|
|
@@ -8612,9 +9508,9 @@ function isRecord5(value) {
|
|
|
8612
9508
|
function uniqueSorted(values) {
|
|
8613
9509
|
return Array.from(new Set(values)).sort((a, b) => a.localeCompare(b));
|
|
8614
9510
|
}
|
|
8615
|
-
async function
|
|
9511
|
+
async function runCommand3(command, args, timeoutMs = 3e3) {
|
|
8616
9512
|
return new Promise((resolve, reject) => {
|
|
8617
|
-
const child = (0,
|
|
9513
|
+
const child = (0, import_node_child_process8.spawn)(command, args, {
|
|
8618
9514
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8619
9515
|
env: process.env
|
|
8620
9516
|
});
|
|
@@ -8649,7 +9545,7 @@ async function runCodexCommand(args) {
|
|
|
8649
9545
|
const isJsLauncher = codexPath.endsWith(".js");
|
|
8650
9546
|
const command = isJsLauncher ? process.execPath : codexPath;
|
|
8651
9547
|
const commandArgs = isJsLauncher ? [codexPath, ...args] : args;
|
|
8652
|
-
return
|
|
9548
|
+
return runCommand3(command, commandArgs);
|
|
8653
9549
|
}
|
|
8654
9550
|
function parseFeatureCatalog(output) {
|
|
8655
9551
|
const entries = [];
|
|
@@ -8690,8 +9586,8 @@ function parseSchemaKeys(schemaRaw) {
|
|
|
8690
9586
|
async function readSchemaFromLocal() {
|
|
8691
9587
|
const candidates = [
|
|
8692
9588
|
process.env.CODEX_CONFIG_SCHEMA_PATH,
|
|
8693
|
-
|
|
8694
|
-
|
|
9589
|
+
import_node_path12.default.join(process.cwd(), "codex-rs", "core", "config.schema.json"),
|
|
9590
|
+
import_node_path12.default.join(process.cwd(), "node_modules", "@openai", "codex", "codex-rs", "core", "config.schema.json")
|
|
8695
9591
|
].filter((candidate) => Boolean(candidate));
|
|
8696
9592
|
for (const candidate of candidates) {
|
|
8697
9593
|
try {
|
|
@@ -8796,20 +9692,20 @@ async function getCodexConfigMetadata(forceRefresh = false) {
|
|
|
8796
9692
|
|
|
8797
9693
|
// ../../packages/runtime-codex/src/codex/config-io.ts
|
|
8798
9694
|
var import_promises2 = require("fs/promises");
|
|
8799
|
-
var
|
|
9695
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
8800
9696
|
|
|
8801
9697
|
// ../../packages/runtime-codex/src/codex/home.ts
|
|
8802
|
-
var
|
|
8803
|
-
var
|
|
9698
|
+
var import_node_os6 = __toESM(require("os"), 1);
|
|
9699
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
8804
9700
|
function resolveHomeDir() {
|
|
8805
|
-
return process.env.HOME || process.env.USERPROFILE ||
|
|
9701
|
+
return process.env.HOME || process.env.USERPROFILE || import_node_os6.default.homedir();
|
|
8806
9702
|
}
|
|
8807
9703
|
function expandHomePrefix(input, homeDir) {
|
|
8808
9704
|
if (input === "~") {
|
|
8809
9705
|
return homeDir;
|
|
8810
9706
|
}
|
|
8811
9707
|
if (input.startsWith("~/") || input.startsWith("~\\")) {
|
|
8812
|
-
return
|
|
9708
|
+
return import_node_path13.default.join(homeDir, input.slice(2));
|
|
8813
9709
|
}
|
|
8814
9710
|
return input;
|
|
8815
9711
|
}
|
|
@@ -8825,13 +9721,10 @@ function normalizeCodexHomePath(input) {
|
|
|
8825
9721
|
if (!configured) {
|
|
8826
9722
|
return null;
|
|
8827
9723
|
}
|
|
8828
|
-
return
|
|
9724
|
+
return import_node_path13.default.resolve(expandHomePrefix(configured, resolveHomeDir()));
|
|
8829
9725
|
}
|
|
8830
9726
|
function resolveDefaultCodexHomeDir() {
|
|
8831
|
-
return
|
|
8832
|
-
}
|
|
8833
|
-
function resolveProfileCodexHomeDir(profileName) {
|
|
8834
|
-
return import_node_path12.default.join(getUserDataDir(), "profile-homes", profileName);
|
|
9727
|
+
return import_node_path13.default.join(resolveHomeDir(), ".codex");
|
|
8835
9728
|
}
|
|
8836
9729
|
function resolveCodexHomeDir(options) {
|
|
8837
9730
|
return normalizeCodexHomePath(options?.codexHomePath) ?? resolveDefaultCodexHomeDir();
|
|
@@ -8846,14 +9739,6 @@ async function resolveCodexRuntimeContext(options) {
|
|
|
8846
9739
|
};
|
|
8847
9740
|
}
|
|
8848
9741
|
const state = await getAppState();
|
|
8849
|
-
const profileName = normalizePathCandidate(state.app.lastProfileName);
|
|
8850
|
-
if (profileName && state.profilesByName[profileName]) {
|
|
8851
|
-
return {
|
|
8852
|
-
codexHomePath: resolveProfileCodexHomeDir(profileName),
|
|
8853
|
-
profileName,
|
|
8854
|
-
source: "profile"
|
|
8855
|
-
};
|
|
8856
|
-
}
|
|
8857
9742
|
const configured = normalizeCodexHomePath(
|
|
8858
9743
|
state.runtimeSettings?.codexHome
|
|
8859
9744
|
);
|
|
@@ -8912,10 +9797,10 @@ var YOLO_PRESET = {
|
|
|
8912
9797
|
|
|
8913
9798
|
// ../../packages/runtime-codex/src/codex/config-io.ts
|
|
8914
9799
|
function getConfigPath(options) {
|
|
8915
|
-
return
|
|
9800
|
+
return import_node_path14.default.join(resolveCodexHomeDir(options), "config.toml");
|
|
8916
9801
|
}
|
|
8917
9802
|
async function ensureConfigDirExists(filePath) {
|
|
8918
|
-
const dir =
|
|
9803
|
+
const dir = import_node_path14.default.dirname(filePath);
|
|
8919
9804
|
await (0, import_promises2.mkdir)(dir, { recursive: true });
|
|
8920
9805
|
}
|
|
8921
9806
|
function normalizeLineEndings2(content) {
|
|
@@ -9569,13 +10454,13 @@ function formatMegabytes(bytes) {
|
|
|
9569
10454
|
return (bytes / MB_DIVISOR).toFixed(2);
|
|
9570
10455
|
}
|
|
9571
10456
|
function resolveSyncBackupsDir() {
|
|
9572
|
-
return
|
|
10457
|
+
return import_node_path15.default.join(getUserDataDir(), SYNC_BACKUP_DIR);
|
|
9573
10458
|
}
|
|
9574
10459
|
function toSafeIsoForFileName(iso) {
|
|
9575
10460
|
return iso.replace(/:/g, "-");
|
|
9576
10461
|
}
|
|
9577
10462
|
async function pruneOldPrePullBackups(backupsDir) {
|
|
9578
|
-
const entries = await
|
|
10463
|
+
const entries = await import_node_fs9.promises.readdir(backupsDir, { withFileTypes: true });
|
|
9579
10464
|
const files = entries.filter(
|
|
9580
10465
|
(entry) => entry.isFile() && entry.name.startsWith(PRE_PULL_BACKUP_PREFIX) && entry.name.endsWith(PRE_PULL_BACKUP_SUFFIX)
|
|
9581
10466
|
).map((entry) => entry.name).sort((a, b) => b.localeCompare(a));
|
|
@@ -9583,7 +10468,7 @@ async function pruneOldPrePullBackups(backupsDir) {
|
|
|
9583
10468
|
return;
|
|
9584
10469
|
}
|
|
9585
10470
|
for (const stale of files.slice(PRE_PULL_BACKUP_KEEP_COUNT)) {
|
|
9586
|
-
await
|
|
10471
|
+
await import_node_fs9.promises.rm(import_node_path15.default.join(backupsDir, stale), { force: true });
|
|
9587
10472
|
}
|
|
9588
10473
|
}
|
|
9589
10474
|
async function createPrePullBackup(profileManager) {
|
|
@@ -9591,13 +10476,13 @@ async function createPrePullBackup(profileManager) {
|
|
|
9591
10476
|
enforcePushGuards: false
|
|
9592
10477
|
});
|
|
9593
10478
|
const backupsDir = resolveSyncBackupsDir();
|
|
9594
|
-
await
|
|
10479
|
+
await import_node_fs9.promises.mkdir(backupsDir, { recursive: true });
|
|
9595
10480
|
const timestamp = toSafeIsoForFileName((/* @__PURE__ */ new Date()).toISOString());
|
|
9596
|
-
const backupPath =
|
|
10481
|
+
const backupPath = import_node_path15.default.join(
|
|
9597
10482
|
backupsDir,
|
|
9598
10483
|
`${PRE_PULL_BACKUP_PREFIX}${timestamp}${PRE_PULL_BACKUP_SUFFIX}`
|
|
9599
10484
|
);
|
|
9600
|
-
await
|
|
10485
|
+
await import_node_fs9.promises.writeFile(backupPath, `${JSON.stringify(snapshot, null, 2)}
|
|
9601
10486
|
`, "utf8");
|
|
9602
10487
|
await pruneOldPrePullBackups(backupsDir);
|
|
9603
10488
|
logInfo("[cloud-sync] created pre-pull backup", { backupPath });
|
|
@@ -9866,9 +10751,9 @@ async function handleSync(args, version) {
|
|
|
9866
10751
|
}
|
|
9867
10752
|
|
|
9868
10753
|
// ../../packages/runtime-app-state/src/storage/migrations/v1.ts
|
|
9869
|
-
var
|
|
9870
|
-
var
|
|
9871
|
-
var
|
|
10754
|
+
var import_node_fs10 = require("fs");
|
|
10755
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
10756
|
+
var import_node_os7 = __toESM(require("os"), 1);
|
|
9872
10757
|
|
|
9873
10758
|
// ../../packages/contracts/src/settings/legacy-localstorage-keys.ts
|
|
9874
10759
|
var LEGACY_LOCALSTORAGE_KEYS = [
|
|
@@ -9907,20 +10792,20 @@ function isMissingPathError(error) {
|
|
|
9907
10792
|
return error.code === "ENOENT";
|
|
9908
10793
|
}
|
|
9909
10794
|
function resolveHomeDir2() {
|
|
9910
|
-
const home = process.env.HOME || process.env.USERPROFILE ||
|
|
10795
|
+
const home = process.env.HOME || process.env.USERPROFILE || import_node_os7.default.homedir();
|
|
9911
10796
|
if (!home) {
|
|
9912
10797
|
throw new Error("HOME is not set.");
|
|
9913
10798
|
}
|
|
9914
10799
|
return home;
|
|
9915
10800
|
}
|
|
9916
10801
|
function resolveCodexDir() {
|
|
9917
|
-
return
|
|
10802
|
+
return import_node_path16.default.join(resolveHomeDir2(), ".codex");
|
|
9918
10803
|
}
|
|
9919
10804
|
function resolveLegacyPath(...segments) {
|
|
9920
|
-
return
|
|
10805
|
+
return import_node_path16.default.join(resolveCodexDir(), ...segments);
|
|
9921
10806
|
}
|
|
9922
10807
|
async function readJsonFile(filePath) {
|
|
9923
|
-
const raw = await
|
|
10808
|
+
const raw = await import_node_fs10.promises.readFile(filePath, "utf8");
|
|
9924
10809
|
return JSON.parse(raw);
|
|
9925
10810
|
}
|
|
9926
10811
|
async function readJsonFileIfExists(filePath) {
|
|
@@ -9935,14 +10820,14 @@ async function readJsonFileIfExists(filePath) {
|
|
|
9935
10820
|
}
|
|
9936
10821
|
}
|
|
9937
10822
|
async function copyDirectoryManual(source, destination) {
|
|
9938
|
-
await
|
|
9939
|
-
const entries = await
|
|
10823
|
+
await import_node_fs10.promises.mkdir(destination, { recursive: true });
|
|
10824
|
+
const entries = await import_node_fs10.promises.readdir(source, { withFileTypes: true });
|
|
9940
10825
|
for (const entry of entries) {
|
|
9941
|
-
const srcPath =
|
|
9942
|
-
const destPath =
|
|
10826
|
+
const srcPath = import_node_path16.default.join(source, entry.name);
|
|
10827
|
+
const destPath = import_node_path16.default.join(destination, entry.name);
|
|
9943
10828
|
if (entry.isSymbolicLink()) {
|
|
9944
|
-
const linkTarget = await
|
|
9945
|
-
await
|
|
10829
|
+
const linkTarget = await import_node_fs10.promises.readlink(srcPath);
|
|
10830
|
+
await import_node_fs10.promises.symlink(linkTarget, destPath).catch((error) => {
|
|
9946
10831
|
if (error.code !== "EEXIST") {
|
|
9947
10832
|
throw error;
|
|
9948
10833
|
}
|
|
@@ -9954,14 +10839,14 @@ async function copyDirectoryManual(source, destination) {
|
|
|
9954
10839
|
continue;
|
|
9955
10840
|
}
|
|
9956
10841
|
if (entry.isFile()) {
|
|
9957
|
-
await
|
|
10842
|
+
await import_node_fs10.promises.copyFile(srcPath, destPath);
|
|
9958
10843
|
}
|
|
9959
10844
|
}
|
|
9960
10845
|
}
|
|
9961
10846
|
async function copyDirIfExists(source, destination) {
|
|
9962
10847
|
let stats = null;
|
|
9963
10848
|
try {
|
|
9964
|
-
stats = await
|
|
10849
|
+
stats = await import_node_fs10.promises.stat(source);
|
|
9965
10850
|
} catch (error) {
|
|
9966
10851
|
if (error.code === "ENOENT") {
|
|
9967
10852
|
return;
|
|
@@ -9971,10 +10856,10 @@ async function copyDirIfExists(source, destination) {
|
|
|
9971
10856
|
if (!stats.isDirectory()) {
|
|
9972
10857
|
return;
|
|
9973
10858
|
}
|
|
9974
|
-
await
|
|
9975
|
-
await
|
|
10859
|
+
await import_node_fs10.promises.rm(destination, { recursive: true, force: true });
|
|
10860
|
+
await import_node_fs10.promises.mkdir(import_node_path16.default.dirname(destination), { recursive: true });
|
|
9976
10861
|
try {
|
|
9977
|
-
await
|
|
10862
|
+
await import_node_fs10.promises.cp(source, destination, {
|
|
9978
10863
|
recursive: true,
|
|
9979
10864
|
errorOnExist: false,
|
|
9980
10865
|
force: true,
|
|
@@ -9991,7 +10876,7 @@ async function copyDirIfExists(source, destination) {
|
|
|
9991
10876
|
}
|
|
9992
10877
|
async function copyFileIfExists(source, destination, options) {
|
|
9993
10878
|
try {
|
|
9994
|
-
const sourceStat = await
|
|
10879
|
+
const sourceStat = await import_node_fs10.promises.stat(source);
|
|
9995
10880
|
if (!sourceStat.isFile()) {
|
|
9996
10881
|
return;
|
|
9997
10882
|
}
|
|
@@ -10002,7 +10887,7 @@ async function copyFileIfExists(source, destination, options) {
|
|
|
10002
10887
|
throw error;
|
|
10003
10888
|
}
|
|
10004
10889
|
try {
|
|
10005
|
-
const destinationStat = await
|
|
10890
|
+
const destinationStat = await import_node_fs10.promises.stat(destination);
|
|
10006
10891
|
if (destinationStat.isFile()) {
|
|
10007
10892
|
return;
|
|
10008
10893
|
}
|
|
@@ -10011,17 +10896,17 @@ async function copyFileIfExists(source, destination, options) {
|
|
|
10011
10896
|
throw error;
|
|
10012
10897
|
}
|
|
10013
10898
|
}
|
|
10014
|
-
await
|
|
10015
|
-
await
|
|
10899
|
+
await import_node_fs10.promises.mkdir(import_node_path16.default.dirname(destination), { recursive: true });
|
|
10900
|
+
await import_node_fs10.promises.copyFile(source, destination);
|
|
10016
10901
|
if (typeof options?.mode === "number") {
|
|
10017
|
-
await
|
|
10902
|
+
await import_node_fs10.promises.chmod(destination, options.mode).catch(() => void 0);
|
|
10018
10903
|
}
|
|
10019
10904
|
}
|
|
10020
10905
|
async function cleanupCopiedSkillsMetadata(skillsDir) {
|
|
10021
|
-
await removeIfExists(
|
|
10906
|
+
await removeIfExists(import_node_path16.default.join(skillsDir, LEGACY_SKILLS_REPOS_FILE));
|
|
10022
10907
|
let entries = [];
|
|
10023
10908
|
try {
|
|
10024
|
-
entries = await
|
|
10909
|
+
entries = await import_node_fs10.promises.readdir(skillsDir, { withFileTypes: true });
|
|
10025
10910
|
} catch {
|
|
10026
10911
|
return;
|
|
10027
10912
|
}
|
|
@@ -10029,30 +10914,30 @@ async function cleanupCopiedSkillsMetadata(skillsDir) {
|
|
|
10029
10914
|
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
10030
10915
|
continue;
|
|
10031
10916
|
}
|
|
10032
|
-
await removeIfExists(
|
|
10917
|
+
await removeIfExists(import_node_path16.default.join(skillsDir, entry.name, LEGACY_SKILL_MANIFEST));
|
|
10033
10918
|
}
|
|
10034
10919
|
}
|
|
10035
10920
|
async function migrateLegacyDirectoriesToUserData() {
|
|
10036
10921
|
const userDataDir = getUserDataDir();
|
|
10037
10922
|
const legacyCodexDir = resolveCodexDir();
|
|
10038
10923
|
await copyDirIfExists(
|
|
10039
|
-
|
|
10040
|
-
|
|
10924
|
+
import_node_path16.default.join(legacyCodexDir, LEGACY_PROFILE_HOMES_DIR),
|
|
10925
|
+
import_node_path16.default.join(userDataDir, LEGACY_PROFILE_HOMES_DIR)
|
|
10041
10926
|
);
|
|
10042
10927
|
await copyDirIfExists(
|
|
10043
|
-
|
|
10044
|
-
|
|
10928
|
+
import_node_path16.default.join(legacyCodexDir, LEGACY_SKILLS_DIR),
|
|
10929
|
+
import_node_path16.default.join(userDataDir, LEGACY_SKILLS_DIR)
|
|
10045
10930
|
);
|
|
10046
10931
|
await copyDirIfExists(
|
|
10047
|
-
|
|
10048
|
-
|
|
10932
|
+
import_node_path16.default.join(legacyCodexDir, LEGACY_SKILL_CACHE_DIR),
|
|
10933
|
+
import_node_path16.default.join(userDataDir, LEGACY_SKILL_CACHE_DIR)
|
|
10049
10934
|
);
|
|
10050
10935
|
await copyFileIfExists(
|
|
10051
|
-
|
|
10052
|
-
|
|
10936
|
+
import_node_path16.default.join(legacyCodexDir, LEGACY_LICENSE_SECRET_FILE2),
|
|
10937
|
+
import_node_path16.default.join(userDataDir, LEGACY_LICENSE_SECRET_FILE2),
|
|
10053
10938
|
{ mode: 384 }
|
|
10054
10939
|
);
|
|
10055
|
-
await cleanupCopiedSkillsMetadata(
|
|
10940
|
+
await cleanupCopiedSkillsMetadata(import_node_path16.default.join(userDataDir, LEGACY_SKILLS_DIR));
|
|
10056
10941
|
}
|
|
10057
10942
|
function pickAutoRoll(raw) {
|
|
10058
10943
|
if (!raw) {
|
|
@@ -10137,9 +11022,13 @@ async function loadLegacySettingsPatch() {
|
|
|
10137
11022
|
license,
|
|
10138
11023
|
autoRoll: autoRoll ? {
|
|
10139
11024
|
enabled: autoRoll.enabled,
|
|
10140
|
-
|
|
10141
|
-
|
|
10142
|
-
restartOfficialCodexOnAutoRoll: autoRoll.restartOfficialCodexOnAutoRoll
|
|
11025
|
+
rearmRemainingThreshold: autoRoll.rearmRemainingThreshold,
|
|
11026
|
+
switchRemainingThreshold: autoRoll.switchRemainingThreshold,
|
|
11027
|
+
restartOfficialCodexOnAutoRoll: autoRoll.restartOfficialCodexOnAutoRoll,
|
|
11028
|
+
launchOfficialCodexWhenClosedOnAutoRoll: autoRoll.launchOfficialCodexWhenClosedOnAutoRoll,
|
|
11029
|
+
priorityOrder: autoRoll.priorityOrder,
|
|
11030
|
+
lowRemainingNotificationEnabled: autoRoll.lowRemainingNotificationEnabled,
|
|
11031
|
+
lowRemainingNotificationThreshold: autoRoll.lowRemainingNotificationThreshold
|
|
10143
11032
|
} : void 0
|
|
10144
11033
|
};
|
|
10145
11034
|
}
|
|
@@ -10159,7 +11048,7 @@ async function loadLegacySyncPatch() {
|
|
|
10159
11048
|
};
|
|
10160
11049
|
}
|
|
10161
11050
|
async function loadLegacyAppSettingsParityPatch() {
|
|
10162
|
-
const filePath =
|
|
11051
|
+
const filePath = import_node_path16.default.join(getUserDataDir(), LEGACY_APP_SETTINGS_PARITY_FILE);
|
|
10163
11052
|
const raw = await readJsonFileIfExists(filePath);
|
|
10164
11053
|
if (!isRecord6(raw)) {
|
|
10165
11054
|
return {};
|
|
@@ -10172,7 +11061,7 @@ async function loadLegacyProfilesPatch() {
|
|
|
10172
11061
|
const root = resolveLegacyPath(LEGACY_PROFILE_HOMES_DIR);
|
|
10173
11062
|
let entries = [];
|
|
10174
11063
|
try {
|
|
10175
|
-
entries = await
|
|
11064
|
+
entries = await import_node_fs10.promises.readdir(root, { withFileTypes: true });
|
|
10176
11065
|
} catch (error) {
|
|
10177
11066
|
if (error.code === "ENOENT") {
|
|
10178
11067
|
return {};
|
|
@@ -10184,7 +11073,7 @@ async function loadLegacyProfilesPatch() {
|
|
|
10184
11073
|
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
10185
11074
|
continue;
|
|
10186
11075
|
}
|
|
10187
|
-
const profileFile =
|
|
11076
|
+
const profileFile = import_node_path16.default.join(root, entry.name, "profile.json");
|
|
10188
11077
|
const raw = await readJsonFileIfExists(profileFile);
|
|
10189
11078
|
if (!raw) {
|
|
10190
11079
|
continue;
|
|
@@ -10220,12 +11109,12 @@ function parseSkillInstallMetadata(raw) {
|
|
|
10220
11109
|
}
|
|
10221
11110
|
async function loadLegacySkillsPatch() {
|
|
10222
11111
|
const skillsRoot = resolveLegacyPath(LEGACY_SKILLS_DIR);
|
|
10223
|
-
const reposPath =
|
|
11112
|
+
const reposPath = import_node_path16.default.join(skillsRoot, LEGACY_SKILLS_REPOS_FILE);
|
|
10224
11113
|
const reposRaw = await readJsonFileIfExists(reposPath);
|
|
10225
11114
|
const installsBySlug = {};
|
|
10226
11115
|
let dirEntries = [];
|
|
10227
11116
|
try {
|
|
10228
|
-
dirEntries = await
|
|
11117
|
+
dirEntries = await import_node_fs10.promises.readdir(skillsRoot, { withFileTypes: true });
|
|
10229
11118
|
} catch (error) {
|
|
10230
11119
|
if (error.code !== "ENOENT") {
|
|
10231
11120
|
throw error;
|
|
@@ -10238,7 +11127,7 @@ async function loadLegacySkillsPatch() {
|
|
|
10238
11127
|
if (entry.name.startsWith(".")) {
|
|
10239
11128
|
continue;
|
|
10240
11129
|
}
|
|
10241
|
-
const manifestPath =
|
|
11130
|
+
const manifestPath = import_node_path16.default.join(skillsRoot, entry.name, LEGACY_SKILL_MANIFEST);
|
|
10242
11131
|
const manifestRaw = await readJsonFileIfExists(manifestPath);
|
|
10243
11132
|
const parsed = parseSkillInstallMetadata(manifestRaw);
|
|
10244
11133
|
if (!parsed) {
|
|
@@ -10334,9 +11223,13 @@ function mergeLegacyLocalStoragePatch(payload) {
|
|
|
10334
11223
|
if (autoRoll) {
|
|
10335
11224
|
patch.autoRoll = {
|
|
10336
11225
|
enabled: autoRoll.enabled,
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
restartOfficialCodexOnAutoRoll: autoRoll.restartOfficialCodexOnAutoRoll
|
|
11226
|
+
rearmRemainingThreshold: autoRoll.rearmRemainingThreshold,
|
|
11227
|
+
switchRemainingThreshold: autoRoll.switchRemainingThreshold,
|
|
11228
|
+
restartOfficialCodexOnAutoRoll: autoRoll.restartOfficialCodexOnAutoRoll,
|
|
11229
|
+
launchOfficialCodexWhenClosedOnAutoRoll: autoRoll.launchOfficialCodexWhenClosedOnAutoRoll,
|
|
11230
|
+
priorityOrder: autoRoll.priorityOrder,
|
|
11231
|
+
lowRemainingNotificationEnabled: autoRoll.lowRemainingNotificationEnabled,
|
|
11232
|
+
lowRemainingNotificationThreshold: autoRoll.lowRemainingNotificationThreshold
|
|
10340
11233
|
};
|
|
10341
11234
|
markSkippedIfPresent("codex:auto-roll-settings", true);
|
|
10342
11235
|
} else {
|
|
@@ -10349,13 +11242,13 @@ function mergeLegacyLocalStoragePatch(payload) {
|
|
|
10349
11242
|
};
|
|
10350
11243
|
}
|
|
10351
11244
|
async function removeIfExists(target) {
|
|
10352
|
-
await
|
|
11245
|
+
await import_node_fs10.promises.rm(target, { recursive: true, force: true });
|
|
10353
11246
|
}
|
|
10354
11247
|
async function cleanupLegacyCanonicalSources() {
|
|
10355
11248
|
const homeDir = resolveHomeDir2();
|
|
10356
11249
|
await removeIfExists(resolveLegacyAppStatePath());
|
|
10357
|
-
await removeIfExists(
|
|
10358
|
-
await removeIfExists(
|
|
11250
|
+
await removeIfExists(import_node_path16.default.join(getUserDataDir(), LEGACY_APP_SETTINGS_PARITY_FILE));
|
|
11251
|
+
await removeIfExists(import_node_path16.default.join(homeDir, SQLITE_STORAGE_DIR));
|
|
10359
11252
|
await removeIfExists(resolveLegacyPath(LEGACY_SETTINGS_FILE));
|
|
10360
11253
|
await removeIfExists(resolveLegacyPath(LEGACY_SETTINGS_BACKUP_FILE));
|
|
10361
11254
|
await removeIfExists(resolveLegacyPath(LEGACY_SYNC_STATE_FILE));
|
|
@@ -10363,15 +11256,15 @@ async function cleanupLegacyCanonicalSources() {
|
|
|
10363
11256
|
await removeIfExists(resolveLegacyPath(LEGACY_SKILLS_DIR, LEGACY_SKILLS_REPOS_FILE));
|
|
10364
11257
|
const profileHomesRoot = resolveLegacyPath(LEGACY_PROFILE_HOMES_DIR);
|
|
10365
11258
|
try {
|
|
10366
|
-
const profileHomes = await
|
|
11259
|
+
const profileHomes = await import_node_fs10.promises.readdir(profileHomesRoot, { withFileTypes: true });
|
|
10367
11260
|
for (const profileHome of profileHomes) {
|
|
10368
11261
|
if (!profileHome.isDirectory()) {
|
|
10369
11262
|
continue;
|
|
10370
11263
|
}
|
|
10371
|
-
const profileDir =
|
|
11264
|
+
const profileDir = import_node_path16.default.join(profileHomesRoot, profileHome.name);
|
|
10372
11265
|
let files = [];
|
|
10373
11266
|
try {
|
|
10374
|
-
files = await
|
|
11267
|
+
files = await import_node_fs10.promises.readdir(profileDir);
|
|
10375
11268
|
} catch {
|
|
10376
11269
|
continue;
|
|
10377
11270
|
}
|
|
@@ -10379,7 +11272,7 @@ async function cleanupLegacyCanonicalSources() {
|
|
|
10379
11272
|
if (!file.startsWith("profile.json")) {
|
|
10380
11273
|
continue;
|
|
10381
11274
|
}
|
|
10382
|
-
await removeIfExists(
|
|
11275
|
+
await removeIfExists(import_node_path16.default.join(profileDir, file));
|
|
10383
11276
|
}
|
|
10384
11277
|
}
|
|
10385
11278
|
} catch (error) {
|
|
@@ -10392,12 +11285,12 @@ async function cleanupLegacyCanonicalSources() {
|
|
|
10392
11285
|
}
|
|
10393
11286
|
const skillsRoot = resolveLegacyPath(LEGACY_SKILLS_DIR);
|
|
10394
11287
|
try {
|
|
10395
|
-
const skillDirs = await
|
|
11288
|
+
const skillDirs = await import_node_fs10.promises.readdir(skillsRoot, { withFileTypes: true });
|
|
10396
11289
|
for (const entry of skillDirs) {
|
|
10397
11290
|
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
10398
11291
|
continue;
|
|
10399
11292
|
}
|
|
10400
|
-
await removeIfExists(
|
|
11293
|
+
await removeIfExists(import_node_path16.default.join(skillsRoot, entry.name, LEGACY_SKILL_MANIFEST));
|
|
10401
11294
|
}
|
|
10402
11295
|
} catch (error) {
|
|
10403
11296
|
if (!isMissingPathError(error)) {
|
|
@@ -10610,7 +11503,7 @@ async function ensureCliStorageReady() {
|
|
|
10610
11503
|
}
|
|
10611
11504
|
|
|
10612
11505
|
// src/app/main.ts
|
|
10613
|
-
var VERSION = true ? "3.9.
|
|
11506
|
+
var VERSION = true ? "3.9.7" : "0.0.0";
|
|
10614
11507
|
async function runCli() {
|
|
10615
11508
|
const args = process.argv.slice(2);
|
|
10616
11509
|
if (args.length === 0) {
|