codexuse-cli 3.9.7 → 3.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +874 -177
- package/dist/index.js.map +1 -1
- package/dist/server/index.mjs +854 -218
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2478,7 +2478,9 @@ function createDefaultAppState() {
|
|
|
2478
2478
|
lastObservedPid: null,
|
|
2479
2479
|
lastRestartStatus: null,
|
|
2480
2480
|
lastRestartReason: null,
|
|
2481
|
-
|
|
2481
|
+
activity: [],
|
|
2482
|
+
instancesByProfileName: {},
|
|
2483
|
+
pendingRestartDebt: null
|
|
2482
2484
|
},
|
|
2483
2485
|
app: {
|
|
2484
2486
|
lastAppVersion: null,
|
|
@@ -2582,6 +2584,9 @@ function asString(value) {
|
|
|
2582
2584
|
function asNumberOrNull(value) {
|
|
2583
2585
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
2584
2586
|
}
|
|
2587
|
+
function asBooleanOrNull(value) {
|
|
2588
|
+
return typeof value === "boolean" ? value : null;
|
|
2589
|
+
}
|
|
2585
2590
|
function normalizeAppState(raw) {
|
|
2586
2591
|
const defaults = createDefaultAppState();
|
|
2587
2592
|
if (!isRecord2(raw)) {
|
|
@@ -2616,6 +2621,30 @@ function normalizeAppState(raw) {
|
|
|
2616
2621
|
merged.officialCodex.lastRestartReason = asString(
|
|
2617
2622
|
merged.officialCodex.lastRestartReason
|
|
2618
2623
|
);
|
|
2624
|
+
merged.officialCodex.activity = Array.isArray(merged.officialCodex.activity) ? merged.officialCodex.activity.filter((entry) => isRecord2(entry)).map((entry) => ({
|
|
2625
|
+
id: asString(entry.id) ?? "",
|
|
2626
|
+
at: asString(entry.at) ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
2627
|
+
kind: entry.kind === "auto-roll-eval" || entry.kind === "profile-switch" || entry.kind === "auth-verified" || entry.kind === "official-codex-restart" || entry.kind === "low-remaining-alert" ? entry.kind : "auto-roll-eval",
|
|
2628
|
+
status: asString(entry.status) ?? "unknown",
|
|
2629
|
+
reason: asString(entry.reason),
|
|
2630
|
+
decisionId: asString(entry.decisionId),
|
|
2631
|
+
profileName: asString(entry.profileName),
|
|
2632
|
+
sourceProfileName: asString(entry.sourceProfileName),
|
|
2633
|
+
targetProfileName: asString(entry.targetProfileName),
|
|
2634
|
+
remainingPercent: asNumberOrNull(entry.remainingPercent),
|
|
2635
|
+
threshold: asNumberOrNull(entry.threshold),
|
|
2636
|
+
snapshotAgeMs: asNumberOrNull(entry.snapshotAgeMs),
|
|
2637
|
+
snapshotSource: asString(entry.snapshotSource),
|
|
2638
|
+
phase: asString(entry.phase),
|
|
2639
|
+
pid: asNumberOrNull(entry.pid),
|
|
2640
|
+
profileKeyHash: asString(entry.profileKeyHash),
|
|
2641
|
+
switchVerified: asBooleanOrNull(entry.switchVerified),
|
|
2642
|
+
restartRequested: asBooleanOrNull(entry.restartRequested),
|
|
2643
|
+
restartResult: asString(entry.restartResult),
|
|
2644
|
+
observedProfileName: asString(entry.observedProfileName),
|
|
2645
|
+
observedProfileKeyHash: asString(entry.observedProfileKeyHash),
|
|
2646
|
+
observedProfileMatchSource: asString(entry.observedProfileMatchSource)
|
|
2647
|
+
})).filter((entry) => entry.id && entry.at).slice(-50) : [];
|
|
2619
2648
|
if (!isRecord2(merged.officialCodex.instancesByProfileName)) {
|
|
2620
2649
|
merged.officialCodex.instancesByProfileName = {};
|
|
2621
2650
|
} else {
|
|
@@ -2646,6 +2675,21 @@ function normalizeAppState(raw) {
|
|
|
2646
2675
|
}
|
|
2647
2676
|
merged.officialCodex.instancesByProfileName = nextInstances;
|
|
2648
2677
|
}
|
|
2678
|
+
if (isRecord2(merged.officialCodex.pendingRestartDebt)) {
|
|
2679
|
+
const debt = merged.officialCodex.pendingRestartDebt;
|
|
2680
|
+
const targetProfileName = asString(debt.targetProfileName);
|
|
2681
|
+
merged.officialCodex.pendingRestartDebt = targetProfileName ? {
|
|
2682
|
+
targetProfileName,
|
|
2683
|
+
targetProfileKey: asString(debt.targetProfileKey),
|
|
2684
|
+
sourceProfileName: asString(debt.sourceProfileName),
|
|
2685
|
+
sourceProfileKey: asString(debt.sourceProfileKey),
|
|
2686
|
+
decisionId: asString(debt.decisionId),
|
|
2687
|
+
attempts: asNumberOrNull(debt.attempts) ?? 0,
|
|
2688
|
+
lastReason: asString(debt.lastReason)
|
|
2689
|
+
} : null;
|
|
2690
|
+
} else {
|
|
2691
|
+
merged.officialCodex.pendingRestartDebt = null;
|
|
2692
|
+
}
|
|
2649
2693
|
merged.app.lastAppVersion = asString(merged.app.lastAppVersion);
|
|
2650
2694
|
merged.app.pendingUpdateVersion = asString(merged.app.pendingUpdateVersion);
|
|
2651
2695
|
merged.app.lastProfileName = asString(merged.app.lastProfileName);
|
|
@@ -3985,10 +4029,10 @@ function logError(...args) {
|
|
|
3985
4029
|
console.error(...args);
|
|
3986
4030
|
}
|
|
3987
4031
|
function logInfo(...args) {
|
|
3988
|
-
if (isTestEnv && !isMocked(console.
|
|
4032
|
+
if (isTestEnv && !isMocked(console.info)) {
|
|
3989
4033
|
return;
|
|
3990
4034
|
}
|
|
3991
|
-
console.
|
|
4035
|
+
console.info(...args);
|
|
3992
4036
|
}
|
|
3993
4037
|
|
|
3994
4038
|
// ../../packages/runtime-codex/src/codex/rpc.ts
|
|
@@ -5402,13 +5446,13 @@ var ProfileManager = class {
|
|
|
5402
5446
|
const existingIdentity = this.resolveProfileIdentityFromData(profileName, existing, existingMetadata);
|
|
5403
5447
|
const nextIdentity = this.resolveProfileIdentityFromData(profileName, normalized, metadata);
|
|
5404
5448
|
if (existingIdentity && nextIdentity && existingIdentity !== nextIdentity) {
|
|
5405
|
-
|
|
5449
|
+
logInfo(
|
|
5406
5450
|
`Skipped syncing tokens for profile '${profileName}' because Codex auth switched to a different OpenAI identity.`
|
|
5407
5451
|
);
|
|
5408
5452
|
return;
|
|
5409
5453
|
}
|
|
5410
5454
|
if (this.normalizeWorkspaceId(existing.workspace_id) !== this.normalizeWorkspaceId(normalized.workspace_id)) {
|
|
5411
|
-
|
|
5455
|
+
logInfo(
|
|
5412
5456
|
`Skipped syncing tokens for profile '${profileName}' because Codex auth switched to a different workspace.`
|
|
5413
5457
|
);
|
|
5414
5458
|
return;
|
|
@@ -8000,18 +8044,17 @@ async function assertProfileCreationAllowed(profileManager) {
|
|
|
8000
8044
|
|
|
8001
8045
|
// ../../packages/runtime-codex/src/codex/officialAppRestart.ts
|
|
8002
8046
|
var import_node_child_process7 = require("child_process");
|
|
8047
|
+
var import_node_crypto6 = require("crypto");
|
|
8003
8048
|
var import_node_fs8 = require("fs");
|
|
8004
8049
|
var import_node_os5 = require("os");
|
|
8005
8050
|
var import_node_path11 = __toESM(require("path"), 1);
|
|
8006
|
-
var RESTART_COOLDOWN_MS = 6e4;
|
|
8007
8051
|
var COMMAND_TIMEOUT_MS = 3e3;
|
|
8008
8052
|
var EXIT_WAIT_MS = 1e4;
|
|
8009
8053
|
var LAUNCH_WAIT_MS = 15e3;
|
|
8010
8054
|
var POLL_INTERVAL_MS = 250;
|
|
8011
8055
|
var APP_DISCOVERY_CACHE_TTL_MS = 6e4;
|
|
8012
8056
|
var APP_DISCOVERY_MISS_CACHE_TTL_MS = 5e3;
|
|
8013
|
-
var
|
|
8014
|
-
var lastRestartStartedAt = 0;
|
|
8057
|
+
var OFFICIAL_CODEX_ACTIVITY_LIMIT = 50;
|
|
8015
8058
|
var profileActionLock = Promise.resolve();
|
|
8016
8059
|
var appDiscoveryCache = null;
|
|
8017
8060
|
function logOfficialCodexRestart(level, message, context) {
|
|
@@ -8022,6 +8065,55 @@ function logOfficialCodexRestart(level, message, context) {
|
|
|
8022
8065
|
}
|
|
8023
8066
|
logger(`[official-codex-restart] ${message}`);
|
|
8024
8067
|
}
|
|
8068
|
+
function hashProfileKey(profileKey) {
|
|
8069
|
+
if (!profileKey) {
|
|
8070
|
+
return null;
|
|
8071
|
+
}
|
|
8072
|
+
return (0, import_node_crypto6.createHash)("sha256").update(profileKey).digest("hex").slice(0, 12);
|
|
8073
|
+
}
|
|
8074
|
+
async function recordOfficialCodexActivity(input) {
|
|
8075
|
+
const now = /* @__PURE__ */ new Date();
|
|
8076
|
+
const entry = {
|
|
8077
|
+
id: `${now.getTime().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
|
|
8078
|
+
at: now.toISOString(),
|
|
8079
|
+
kind: input.kind,
|
|
8080
|
+
status: input.status,
|
|
8081
|
+
reason: input.reason ?? null,
|
|
8082
|
+
decisionId: input.decisionId ?? null,
|
|
8083
|
+
profileName: input.profileName ?? null,
|
|
8084
|
+
sourceProfileName: input.sourceProfileName ?? null,
|
|
8085
|
+
targetProfileName: input.targetProfileName ?? null,
|
|
8086
|
+
remainingPercent: input.remainingPercent ?? null,
|
|
8087
|
+
threshold: input.threshold ?? null,
|
|
8088
|
+
snapshotAgeMs: input.snapshotAgeMs ?? null,
|
|
8089
|
+
snapshotSource: input.snapshotSource ?? null,
|
|
8090
|
+
phase: input.phase ?? null,
|
|
8091
|
+
pid: input.pid ?? null,
|
|
8092
|
+
profileKeyHash: input.profileKeyHash ?? hashProfileKey(input.profileKey),
|
|
8093
|
+
switchVerified: input.switchVerified ?? null,
|
|
8094
|
+
restartRequested: input.restartRequested ?? null,
|
|
8095
|
+
restartResult: input.restartResult ?? null,
|
|
8096
|
+
observedProfileName: input.observedProfileName ?? null,
|
|
8097
|
+
observedProfileKeyHash: input.observedProfileKeyHash ?? hashProfileKey(input.observedProfileKey),
|
|
8098
|
+
observedProfileMatchSource: input.observedProfileMatchSource ?? null
|
|
8099
|
+
};
|
|
8100
|
+
try {
|
|
8101
|
+
await updateAppState((state) => ({
|
|
8102
|
+
officialCodex: {
|
|
8103
|
+
activity: [...state.officialCodex.activity, entry].slice(
|
|
8104
|
+
-OFFICIAL_CODEX_ACTIVITY_LIMIT
|
|
8105
|
+
)
|
|
8106
|
+
}
|
|
8107
|
+
}));
|
|
8108
|
+
} catch (error) {
|
|
8109
|
+
logOfficialCodexRestart("warn", "Failed to record official Codex activity.", {
|
|
8110
|
+
kind: entry.kind,
|
|
8111
|
+
status: entry.status,
|
|
8112
|
+
profileKeyHash: entry.profileKeyHash,
|
|
8113
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8114
|
+
});
|
|
8115
|
+
}
|
|
8116
|
+
}
|
|
8025
8117
|
function runCommand2(command, args, timeoutMs = COMMAND_TIMEOUT_MS) {
|
|
8026
8118
|
return new Promise((resolve) => {
|
|
8027
8119
|
const child = (0, import_node_child_process7.spawn)(command, args, {
|
|
@@ -8055,6 +8147,278 @@ function runCommand2(command, args, timeoutMs = COMMAND_TIMEOUT_MS) {
|
|
|
8055
8147
|
});
|
|
8056
8148
|
});
|
|
8057
8149
|
}
|
|
8150
|
+
function enqueueProfileAction(work) {
|
|
8151
|
+
const run = profileActionLock.then(work, work);
|
|
8152
|
+
profileActionLock = run.then(
|
|
8153
|
+
() => void 0,
|
|
8154
|
+
() => void 0
|
|
8155
|
+
);
|
|
8156
|
+
return run;
|
|
8157
|
+
}
|
|
8158
|
+
async function readProcessRows() {
|
|
8159
|
+
const result = await runCommand2(
|
|
8160
|
+
"/bin/ps",
|
|
8161
|
+
["-axo", "pid=,ppid=,lstart=,args="],
|
|
8162
|
+
2e3
|
|
8163
|
+
);
|
|
8164
|
+
if (result.code !== 0) {
|
|
8165
|
+
return [];
|
|
8166
|
+
}
|
|
8167
|
+
return result.stdout.split(/\r?\n/).flatMap((line) => {
|
|
8168
|
+
const match = line.match(
|
|
8169
|
+
/^\s*(\d+)\s+(\d+)\s+([A-Z][a-z]{2}\s+[A-Z][a-z]{2}\s+\d+\s+\d+:\d+:\d+\s+\d{4})\s+(.+)$/
|
|
8170
|
+
);
|
|
8171
|
+
if (!match) {
|
|
8172
|
+
return [];
|
|
8173
|
+
}
|
|
8174
|
+
const pid = Number(match[1]);
|
|
8175
|
+
const ppid = Number(match[2]);
|
|
8176
|
+
const parsedStartedAt = Date.parse(match[3] ?? "");
|
|
8177
|
+
const startedAt = Number.isFinite(parsedStartedAt) ? parsedStartedAt : null;
|
|
8178
|
+
const args = match[4] ?? "";
|
|
8179
|
+
if (!Number.isInteger(pid) || !Number.isInteger(ppid) || !args) {
|
|
8180
|
+
return [];
|
|
8181
|
+
}
|
|
8182
|
+
return [{ pid, ppid, startedAt, args }];
|
|
8183
|
+
});
|
|
8184
|
+
}
|
|
8185
|
+
function getMainExecutablePath(candidate) {
|
|
8186
|
+
return import_node_path11.default.join(candidate.appPath, "Contents", "MacOS", candidate.executableName);
|
|
8187
|
+
}
|
|
8188
|
+
function isMainProcessRow(row, candidate) {
|
|
8189
|
+
return row.args.includes(getMainExecutablePath(candidate));
|
|
8190
|
+
}
|
|
8191
|
+
function findAppServerPid(rows, mainPid) {
|
|
8192
|
+
return rows.find(
|
|
8193
|
+
(row) => row.ppid === mainPid && row.args.includes("/Contents/Resources/codex app-server")
|
|
8194
|
+
)?.pid ?? null;
|
|
8195
|
+
}
|
|
8196
|
+
async function processMatchesProfileHome(pid, profileHome) {
|
|
8197
|
+
const result = await runCommand2(
|
|
8198
|
+
"/bin/ps",
|
|
8199
|
+
["eww", "-p", String(pid), "-o", "command="],
|
|
8200
|
+
2e3
|
|
8201
|
+
);
|
|
8202
|
+
if (result.code !== 0) {
|
|
8203
|
+
return false;
|
|
8204
|
+
}
|
|
8205
|
+
return result.stdout.includes(`CODEX_HOME=${profileHome}`) || result.stdout.includes(profileHome);
|
|
8206
|
+
}
|
|
8207
|
+
async function findAppServerPidForProfile(rows, mainPid, profileHome) {
|
|
8208
|
+
const candidates = rows.filter(
|
|
8209
|
+
(row) => row.ppid === mainPid && row.args.includes("/Contents/Resources/codex app-server")
|
|
8210
|
+
);
|
|
8211
|
+
for (const row of candidates) {
|
|
8212
|
+
if (row.args.includes(profileHome) || await processMatchesProfileHome(row.pid, profileHome)) {
|
|
8213
|
+
return row.pid;
|
|
8214
|
+
}
|
|
8215
|
+
}
|
|
8216
|
+
return null;
|
|
8217
|
+
}
|
|
8218
|
+
async function describeUnmanagedProfileHint(appServerPid) {
|
|
8219
|
+
if (!appServerPid) {
|
|
8220
|
+
return {};
|
|
8221
|
+
}
|
|
8222
|
+
const result = await runCommand2(
|
|
8223
|
+
"/bin/ps",
|
|
8224
|
+
["eww", "-p", String(appServerPid), "-o", "command="],
|
|
8225
|
+
2e3
|
|
8226
|
+
);
|
|
8227
|
+
if (result.code !== 0) {
|
|
8228
|
+
return {};
|
|
8229
|
+
}
|
|
8230
|
+
const telemetryMatch = result.stdout.match(
|
|
8231
|
+
/CODEX_TELEMETRY_LABEL=codexuse-profile-([^\s]+)/
|
|
8232
|
+
);
|
|
8233
|
+
if (telemetryMatch?.[1]) {
|
|
8234
|
+
const encodedProfileName = telemetryMatch[1];
|
|
8235
|
+
let profileName = encodedProfileName;
|
|
8236
|
+
try {
|
|
8237
|
+
profileName = decodeURIComponent(encodedProfileName);
|
|
8238
|
+
} catch {
|
|
8239
|
+
profileName = encodedProfileName;
|
|
8240
|
+
}
|
|
8241
|
+
return {
|
|
8242
|
+
profileName,
|
|
8243
|
+
profileMatchSource: "telemetry-label"
|
|
8244
|
+
};
|
|
8245
|
+
}
|
|
8246
|
+
const profileHomeMatch = result.stdout.match(/profile-homes\/([^\s]+)/);
|
|
8247
|
+
if (profileHomeMatch?.[1]) {
|
|
8248
|
+
return {
|
|
8249
|
+
profileName: profileHomeMatch[1],
|
|
8250
|
+
profileMatchSource: "profile-home"
|
|
8251
|
+
};
|
|
8252
|
+
}
|
|
8253
|
+
return {};
|
|
8254
|
+
}
|
|
8255
|
+
function getDescendantPids(rootPid, rows) {
|
|
8256
|
+
const descendants = [];
|
|
8257
|
+
const queue = [rootPid];
|
|
8258
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8259
|
+
while (queue.length > 0) {
|
|
8260
|
+
const current = queue.shift();
|
|
8261
|
+
if (seen.has(current)) {
|
|
8262
|
+
continue;
|
|
8263
|
+
}
|
|
8264
|
+
seen.add(current);
|
|
8265
|
+
for (const row of rows) {
|
|
8266
|
+
if (row.ppid !== current || seen.has(row.pid)) {
|
|
8267
|
+
continue;
|
|
8268
|
+
}
|
|
8269
|
+
descendants.push(row.pid);
|
|
8270
|
+
queue.push(row.pid);
|
|
8271
|
+
}
|
|
8272
|
+
}
|
|
8273
|
+
return descendants;
|
|
8274
|
+
}
|
|
8275
|
+
async function waitForNewMainPid(candidate, previousPids) {
|
|
8276
|
+
const deadline = Date.now() + LAUNCH_WAIT_MS;
|
|
8277
|
+
while (Date.now() < deadline) {
|
|
8278
|
+
const rows = await readProcessRows();
|
|
8279
|
+
const pid = rows.find((row) => isMainProcessRow(row, candidate) && !previousPids.has(row.pid))?.pid ?? null;
|
|
8280
|
+
if (pid !== null) {
|
|
8281
|
+
return pid;
|
|
8282
|
+
}
|
|
8283
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
8284
|
+
}
|
|
8285
|
+
return null;
|
|
8286
|
+
}
|
|
8287
|
+
async function waitForMainPid(candidate, pid, timeoutMs) {
|
|
8288
|
+
const deadline = Date.now() + timeoutMs;
|
|
8289
|
+
while (Date.now() < deadline) {
|
|
8290
|
+
const rows = await readProcessRows();
|
|
8291
|
+
if (rows.some((row) => row.pid === pid && isMainProcessRow(row, candidate))) {
|
|
8292
|
+
return true;
|
|
8293
|
+
}
|
|
8294
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
8295
|
+
}
|
|
8296
|
+
return false;
|
|
8297
|
+
}
|
|
8298
|
+
async function waitForAppServerPid(mainPid, profileHome) {
|
|
8299
|
+
const deadline = Date.now() + LAUNCH_WAIT_MS;
|
|
8300
|
+
while (Date.now() < deadline) {
|
|
8301
|
+
const pid = await findAppServerPidForProfile(
|
|
8302
|
+
await readProcessRows(),
|
|
8303
|
+
mainPid,
|
|
8304
|
+
profileHome
|
|
8305
|
+
);
|
|
8306
|
+
if (pid !== null) {
|
|
8307
|
+
return pid;
|
|
8308
|
+
}
|
|
8309
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
8310
|
+
}
|
|
8311
|
+
return null;
|
|
8312
|
+
}
|
|
8313
|
+
async function waitForMainPidExit(pid, timeoutMs) {
|
|
8314
|
+
const deadline = Date.now() + timeoutMs;
|
|
8315
|
+
while (Date.now() < deadline) {
|
|
8316
|
+
const rows = await readProcessRows();
|
|
8317
|
+
if (!rows.some((row) => row.pid === pid)) {
|
|
8318
|
+
return true;
|
|
8319
|
+
}
|
|
8320
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
8321
|
+
}
|
|
8322
|
+
return false;
|
|
8323
|
+
}
|
|
8324
|
+
function managedInstanceToPayload(instance, running, appServerPid = instance.appServerPid, startedAt = null) {
|
|
8325
|
+
const runningStartedAt = startedAt ?? instance.launchedAt;
|
|
8326
|
+
return {
|
|
8327
|
+
profileName: instance.profileName,
|
|
8328
|
+
profileKey: instance.profileKey,
|
|
8329
|
+
appPath: instance.appPath,
|
|
8330
|
+
bundleId: instance.bundleId,
|
|
8331
|
+
pid: running ? instance.pid : null,
|
|
8332
|
+
appServerPid: running ? appServerPid : null,
|
|
8333
|
+
running,
|
|
8334
|
+
startedAt: running ? toIso(runningStartedAt) : null,
|
|
8335
|
+
uptimeMs: running && typeof runningStartedAt === "number" ? Math.max(0, Date.now() - runningStartedAt) : null,
|
|
8336
|
+
launchedAt: toIso(instance.launchedAt),
|
|
8337
|
+
lastVerifiedAt: toIso(instance.lastVerifiedAt),
|
|
8338
|
+
lastStatus: instance.lastStatus,
|
|
8339
|
+
lastError: instance.lastError
|
|
8340
|
+
};
|
|
8341
|
+
}
|
|
8342
|
+
async function patchManagedInstance(instance) {
|
|
8343
|
+
await patchAppState({
|
|
8344
|
+
officialCodex: {
|
|
8345
|
+
instancesByProfileName: {
|
|
8346
|
+
[instance.profileName]: instance
|
|
8347
|
+
}
|
|
8348
|
+
}
|
|
8349
|
+
});
|
|
8350
|
+
}
|
|
8351
|
+
async function readManagedInstance(profileName) {
|
|
8352
|
+
const state = await getAppState();
|
|
8353
|
+
return state.officialCodex.instancesByProfileName[profileName] ?? null;
|
|
8354
|
+
}
|
|
8355
|
+
async function resolveInstanceRuntimeState(args) {
|
|
8356
|
+
const pid = args.instance.pid;
|
|
8357
|
+
if (!pid) {
|
|
8358
|
+
return { running: false, appServerPid: null, startedAt: null };
|
|
8359
|
+
}
|
|
8360
|
+
const mainRow = args.rows.find((row) => row.pid === pid);
|
|
8361
|
+
if (!mainRow || !isMainProcessRow(mainRow, args.candidate)) {
|
|
8362
|
+
return { running: false, appServerPid: null, startedAt: null };
|
|
8363
|
+
}
|
|
8364
|
+
const appServerPid = args.instance.profileHome ? await findAppServerPidForProfile(args.rows, pid, args.instance.profileHome) : findAppServerPid(args.rows, pid);
|
|
8365
|
+
if (args.instance.profileHome && appServerPid === null) {
|
|
8366
|
+
return { running: false, appServerPid: null, startedAt: null };
|
|
8367
|
+
}
|
|
8368
|
+
return {
|
|
8369
|
+
running: true,
|
|
8370
|
+
appServerPid,
|
|
8371
|
+
startedAt: mainRow.startedAt
|
|
8372
|
+
};
|
|
8373
|
+
}
|
|
8374
|
+
async function openCodexWithProfileHome(candidate, profileHome, profileName, previousPids) {
|
|
8375
|
+
const executablePath = getMainExecutablePath(candidate);
|
|
8376
|
+
if (!(0, import_node_fs8.existsSync)(executablePath)) {
|
|
8377
|
+
return { opened: false, pid: null };
|
|
8378
|
+
}
|
|
8379
|
+
const telemetryLabel = `codexuse-profile-${encodeURIComponent(profileName)}`;
|
|
8380
|
+
let child;
|
|
8381
|
+
try {
|
|
8382
|
+
child = (0, import_node_child_process7.spawn)(executablePath, [], {
|
|
8383
|
+
detached: true,
|
|
8384
|
+
env: {
|
|
8385
|
+
...process.env,
|
|
8386
|
+
CODEX_HOME: profileHome,
|
|
8387
|
+
CODEX_TELEMETRY_LABEL: telemetryLabel
|
|
8388
|
+
},
|
|
8389
|
+
stdio: "ignore"
|
|
8390
|
+
});
|
|
8391
|
+
} catch {
|
|
8392
|
+
return { opened: false, pid: null };
|
|
8393
|
+
}
|
|
8394
|
+
const childPid = child.pid ?? null;
|
|
8395
|
+
child.unref();
|
|
8396
|
+
const spawnError = await new Promise((resolve) => {
|
|
8397
|
+
let settled = false;
|
|
8398
|
+
const settle = (failed) => {
|
|
8399
|
+
if (settled) {
|
|
8400
|
+
return;
|
|
8401
|
+
}
|
|
8402
|
+
settled = true;
|
|
8403
|
+
resolve(failed);
|
|
8404
|
+
};
|
|
8405
|
+
child.once("error", () => settle(true));
|
|
8406
|
+
setTimeout(() => settle(false), POLL_INTERVAL_MS);
|
|
8407
|
+
});
|
|
8408
|
+
if (spawnError) {
|
|
8409
|
+
return { opened: false, pid: null };
|
|
8410
|
+
}
|
|
8411
|
+
if (childPid !== null) {
|
|
8412
|
+
const verified = await waitForMainPid(candidate, childPid, 2e3);
|
|
8413
|
+
if (verified) {
|
|
8414
|
+
return { opened: true, pid: childPid };
|
|
8415
|
+
}
|
|
8416
|
+
}
|
|
8417
|
+
return {
|
|
8418
|
+
opened: true,
|
|
8419
|
+
pid: await waitForNewMainPid(candidate, previousPids)
|
|
8420
|
+
};
|
|
8421
|
+
}
|
|
8058
8422
|
async function readBundleString(appPath, key) {
|
|
8059
8423
|
const plistPath = import_node_path11.default.join(appPath, "Contents", "Info.plist");
|
|
8060
8424
|
if (!(0, import_node_fs8.existsSync)(plistPath)) {
|
|
@@ -8187,17 +8551,6 @@ async function resolveCodexAppTarget() {
|
|
|
8187
8551
|
]);
|
|
8188
8552
|
return { candidate: fallback, runningPids, mainPids };
|
|
8189
8553
|
}
|
|
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
8554
|
async function signalPids(pids, signal) {
|
|
8202
8555
|
const signaled = [];
|
|
8203
8556
|
for (const pid of pids) {
|
|
@@ -8209,25 +8562,8 @@ async function signalPids(pids, signal) {
|
|
|
8209
8562
|
}
|
|
8210
8563
|
return signaled;
|
|
8211
8564
|
}
|
|
8212
|
-
|
|
8213
|
-
|
|
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;
|
|
8565
|
+
function toIso(value) {
|
|
8566
|
+
return typeof value === "number" && Number.isFinite(value) ? new Date(value).toISOString() : null;
|
|
8231
8567
|
}
|
|
8232
8568
|
async function rememberProfileSwitch(profileKey) {
|
|
8233
8569
|
await patchAppState({
|
|
@@ -8250,157 +8586,359 @@ async function rememberRestartResult(args) {
|
|
|
8250
8586
|
}
|
|
8251
8587
|
});
|
|
8252
8588
|
}
|
|
8253
|
-
|
|
8589
|
+
function recordOfficialCodexRestartResult(args) {
|
|
8590
|
+
return rememberRestartResult(args);
|
|
8591
|
+
}
|
|
8592
|
+
function recordOfficialCodexProfileSwitch(profileKey) {
|
|
8593
|
+
return rememberProfileSwitch(profileKey);
|
|
8594
|
+
}
|
|
8595
|
+
async function getOfficialCodexProfileInstances() {
|
|
8254
8596
|
if (process.platform !== "darwin") {
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
profileKey: options.currentProfileKey
|
|
8264
|
-
});
|
|
8265
|
-
return { status: "skipped", reason: "cooldown" };
|
|
8597
|
+
return {
|
|
8598
|
+
state: "unsupported",
|
|
8599
|
+
appPath: null,
|
|
8600
|
+
bundleId: null,
|
|
8601
|
+
version: null,
|
|
8602
|
+
instances: [],
|
|
8603
|
+
unmanaged: []
|
|
8604
|
+
};
|
|
8266
8605
|
}
|
|
8267
|
-
const { candidate,
|
|
8606
|
+
const { candidate, mainPids } = await resolveCodexAppTarget();
|
|
8268
8607
|
if (!candidate) {
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
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" };
|
|
8608
|
+
return {
|
|
8609
|
+
state: "not-found",
|
|
8610
|
+
appPath: null,
|
|
8611
|
+
bundleId: null,
|
|
8612
|
+
version: null,
|
|
8613
|
+
instances: [],
|
|
8614
|
+
unmanaged: []
|
|
8615
|
+
};
|
|
8284
8616
|
}
|
|
8285
|
-
|
|
8286
|
-
|
|
8617
|
+
const [state, rows] = await Promise.all([getAppState(), readProcessRows()]);
|
|
8618
|
+
const managed = [];
|
|
8619
|
+
for (const instance of Object.values(state.officialCodex.instancesByProfileName)) {
|
|
8620
|
+
const runtime = await resolveInstanceRuntimeState({ instance, candidate, rows });
|
|
8621
|
+
managed.push(
|
|
8622
|
+
managedInstanceToPayload(
|
|
8623
|
+
instance,
|
|
8624
|
+
runtime.running,
|
|
8625
|
+
runtime.appServerPid,
|
|
8626
|
+
runtime.startedAt
|
|
8627
|
+
)
|
|
8628
|
+
);
|
|
8287
8629
|
}
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8630
|
+
managed.sort((a, b) => a.profileName.localeCompare(b.profileName));
|
|
8631
|
+
const managedPids = new Set(
|
|
8632
|
+
managed.flatMap((entry) => entry.running && entry.pid ? [entry.pid] : [])
|
|
8633
|
+
);
|
|
8634
|
+
const unmanaged = [];
|
|
8635
|
+
for (const pid of mainPids.filter((entry) => !managedPids.has(entry))) {
|
|
8636
|
+
const appServerPid = findAppServerPid(rows, pid);
|
|
8637
|
+
const startedAt = rows.find((row) => row.pid === pid)?.startedAt ?? null;
|
|
8638
|
+
unmanaged.push({
|
|
8639
|
+
pid,
|
|
8640
|
+
appServerPid,
|
|
8641
|
+
startedAt: toIso(startedAt),
|
|
8642
|
+
uptimeMs: typeof startedAt === "number" ? Math.max(0, Date.now() - startedAt) : null,
|
|
8643
|
+
...await describeUnmanagedProfileHint(appServerPid)
|
|
8302
8644
|
});
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8645
|
+
}
|
|
8646
|
+
return {
|
|
8647
|
+
state: "found",
|
|
8648
|
+
appPath: candidate.appPath,
|
|
8649
|
+
bundleId: candidate.bundleId,
|
|
8650
|
+
version: candidate.version,
|
|
8651
|
+
instances: managed,
|
|
8652
|
+
unmanaged
|
|
8653
|
+
};
|
|
8654
|
+
}
|
|
8655
|
+
function launchOfficialCodexProfileInstance(options) {
|
|
8656
|
+
return enqueueProfileAction(async () => {
|
|
8657
|
+
if (process.platform !== "darwin") {
|
|
8658
|
+
return {
|
|
8659
|
+
status: "skipped",
|
|
8660
|
+
profileName: options.profileName,
|
|
8661
|
+
instance: null,
|
|
8662
|
+
reason: "unsupported-platform"
|
|
8663
|
+
};
|
|
8664
|
+
}
|
|
8665
|
+
const { candidate, mainPids } = await resolveCodexAppTarget();
|
|
8666
|
+
if (!candidate) {
|
|
8667
|
+
return {
|
|
8668
|
+
status: "skipped",
|
|
8669
|
+
profileName: options.profileName,
|
|
8670
|
+
instance: null,
|
|
8671
|
+
reason: "official-codex-app-not-found"
|
|
8672
|
+
};
|
|
8673
|
+
}
|
|
8674
|
+
const existing = await readManagedInstance(options.profileName);
|
|
8675
|
+
if (existing) {
|
|
8676
|
+
const rows = await readProcessRows();
|
|
8677
|
+
const runtime = await resolveInstanceRuntimeState({ instance: existing, candidate, rows });
|
|
8678
|
+
if (runtime.running) {
|
|
8679
|
+
const verified = {
|
|
8680
|
+
...existing,
|
|
8681
|
+
profileHome: existing.profileHome ?? options.profileHome,
|
|
8682
|
+
appServerPid: runtime.appServerPid,
|
|
8683
|
+
lastVerifiedAt: Date.now(),
|
|
8684
|
+
lastStatus: "already-running",
|
|
8685
|
+
lastError: null
|
|
8686
|
+
};
|
|
8687
|
+
await patchManagedInstance(verified);
|
|
8688
|
+
return {
|
|
8689
|
+
status: "already-running",
|
|
8690
|
+
profileName: options.profileName,
|
|
8691
|
+
instance: managedInstanceToPayload(
|
|
8692
|
+
verified,
|
|
8693
|
+
true,
|
|
8694
|
+
runtime.appServerPid,
|
|
8695
|
+
runtime.startedAt
|
|
8696
|
+
),
|
|
8697
|
+
reason: null
|
|
8698
|
+
};
|
|
8699
|
+
}
|
|
8700
|
+
}
|
|
8701
|
+
const launch = await openCodexWithProfileHome(
|
|
8702
|
+
candidate,
|
|
8703
|
+
options.profileHome,
|
|
8704
|
+
options.profileName,
|
|
8705
|
+
new Set(mainPids)
|
|
8706
|
+
);
|
|
8707
|
+
if (!launch.opened) {
|
|
8708
|
+
const failed = {
|
|
8709
|
+
profileName: options.profileName,
|
|
8710
|
+
profileKey: options.profileKey,
|
|
8711
|
+
profileHome: options.profileHome,
|
|
8307
8712
|
appPath: candidate.appPath,
|
|
8308
8713
|
bundleId: candidate.bundleId,
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8714
|
+
pid: null,
|
|
8715
|
+
appServerPid: null,
|
|
8716
|
+
launchedAt: null,
|
|
8717
|
+
lastVerifiedAt: null,
|
|
8718
|
+
lastStatus: "failed",
|
|
8719
|
+
lastError: "open-failed"
|
|
8720
|
+
};
|
|
8721
|
+
await patchManagedInstance(failed);
|
|
8722
|
+
return {
|
|
8723
|
+
status: "failed",
|
|
8724
|
+
profileName: options.profileName,
|
|
8725
|
+
instance: managedInstanceToPayload(failed, false),
|
|
8726
|
+
reason: "open-failed"
|
|
8727
|
+
};
|
|
8728
|
+
}
|
|
8729
|
+
const launchedPid = launch.pid;
|
|
8730
|
+
if (launchedPid === null) {
|
|
8312
8731
|
const failed = {
|
|
8732
|
+
profileName: options.profileName,
|
|
8733
|
+
profileKey: options.profileKey,
|
|
8734
|
+
profileHome: options.profileHome,
|
|
8735
|
+
appPath: candidate.appPath,
|
|
8736
|
+
bundleId: candidate.bundleId,
|
|
8737
|
+
pid: null,
|
|
8738
|
+
appServerPid: null,
|
|
8739
|
+
launchedAt: null,
|
|
8740
|
+
lastVerifiedAt: null,
|
|
8741
|
+
lastStatus: "failed",
|
|
8742
|
+
lastError: "launch-timeout"
|
|
8743
|
+
};
|
|
8744
|
+
await patchManagedInstance(failed);
|
|
8745
|
+
return {
|
|
8313
8746
|
status: "failed",
|
|
8747
|
+
profileName: options.profileName,
|
|
8748
|
+
instance: managedInstanceToPayload(failed, false),
|
|
8749
|
+
reason: "launch-timeout"
|
|
8750
|
+
};
|
|
8751
|
+
}
|
|
8752
|
+
const appServerPid = await waitForAppServerPid(
|
|
8753
|
+
launchedPid,
|
|
8754
|
+
options.profileHome
|
|
8755
|
+
);
|
|
8756
|
+
if (appServerPid === null) {
|
|
8757
|
+
const failed = {
|
|
8758
|
+
profileName: options.profileName,
|
|
8759
|
+
profileKey: options.profileKey,
|
|
8760
|
+
profileHome: options.profileHome,
|
|
8314
8761
|
appPath: candidate.appPath,
|
|
8315
8762
|
bundleId: candidate.bundleId,
|
|
8316
|
-
|
|
8317
|
-
|
|
8763
|
+
pid: null,
|
|
8764
|
+
appServerPid: null,
|
|
8765
|
+
launchedAt: null,
|
|
8766
|
+
lastVerifiedAt: Date.now(),
|
|
8767
|
+
lastStatus: "failed",
|
|
8768
|
+
lastError: "app-server-not-verified"
|
|
8769
|
+
};
|
|
8770
|
+
await patchManagedInstance(failed);
|
|
8771
|
+
return {
|
|
8772
|
+
status: "failed",
|
|
8773
|
+
profileName: options.profileName,
|
|
8774
|
+
instance: managedInstanceToPayload(failed, false),
|
|
8775
|
+
reason: "app-server-not-verified"
|
|
8318
8776
|
};
|
|
8319
|
-
await rememberRestartResult({
|
|
8320
|
-
status: failed.status,
|
|
8321
|
-
reason: failed.reason,
|
|
8322
|
-
profileKey: options.currentProfileKey
|
|
8323
|
-
});
|
|
8324
|
-
return failed;
|
|
8325
8777
|
}
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
}
|
|
8332
|
-
const opened = await openCodex(candidate);
|
|
8333
|
-
if (!opened) {
|
|
8334
|
-
const failed = {
|
|
8335
|
-
status: "failed",
|
|
8778
|
+
const now = Date.now();
|
|
8779
|
+
const instance = {
|
|
8780
|
+
profileName: options.profileName,
|
|
8781
|
+
profileKey: options.profileKey,
|
|
8782
|
+
profileHome: options.profileHome,
|
|
8336
8783
|
appPath: candidate.appPath,
|
|
8337
8784
|
bundleId: candidate.bundleId,
|
|
8338
|
-
|
|
8339
|
-
|
|
8785
|
+
pid: launchedPid,
|
|
8786
|
+
appServerPid,
|
|
8787
|
+
launchedAt: now,
|
|
8788
|
+
lastVerifiedAt: now,
|
|
8789
|
+
lastStatus: "started",
|
|
8790
|
+
lastError: null
|
|
8340
8791
|
};
|
|
8341
|
-
await
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8346
|
-
|
|
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"
|
|
8792
|
+
await patchManagedInstance(instance);
|
|
8793
|
+
return {
|
|
8794
|
+
status: "started",
|
|
8795
|
+
profileName: options.profileName,
|
|
8796
|
+
instance: managedInstanceToPayload(instance, true, appServerPid),
|
|
8797
|
+
reason: null
|
|
8361
8798
|
};
|
|
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
8799
|
});
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8800
|
+
}
|
|
8801
|
+
function stopOfficialCodexProfileInstance(profileName) {
|
|
8802
|
+
return enqueueProfileAction(async () => {
|
|
8803
|
+
const existing = await readManagedInstance(profileName);
|
|
8804
|
+
if (!existing) {
|
|
8805
|
+
return {
|
|
8806
|
+
status: "not-running",
|
|
8807
|
+
profileName,
|
|
8808
|
+
instance: null,
|
|
8809
|
+
reason: "not-managed"
|
|
8810
|
+
};
|
|
8811
|
+
}
|
|
8812
|
+
const { candidate } = await resolveCodexAppTarget();
|
|
8813
|
+
if (!candidate) {
|
|
8814
|
+
const stopped2 = {
|
|
8815
|
+
...existing,
|
|
8816
|
+
pid: null,
|
|
8817
|
+
appServerPid: null,
|
|
8818
|
+
lastVerifiedAt: Date.now(),
|
|
8819
|
+
lastStatus: "not-running",
|
|
8820
|
+
lastError: "official-codex-app-not-found"
|
|
8821
|
+
};
|
|
8822
|
+
await patchManagedInstance(stopped2);
|
|
8823
|
+
return {
|
|
8824
|
+
status: "not-running",
|
|
8825
|
+
profileName,
|
|
8826
|
+
instance: managedInstanceToPayload(stopped2, false),
|
|
8827
|
+
reason: "official-codex-app-not-found"
|
|
8828
|
+
};
|
|
8829
|
+
}
|
|
8830
|
+
const rows = await readProcessRows();
|
|
8831
|
+
const runtime = await resolveInstanceRuntimeState({ instance: existing, candidate, rows });
|
|
8832
|
+
if (!runtime.running || !existing.pid) {
|
|
8833
|
+
const stopped2 = {
|
|
8834
|
+
...existing,
|
|
8835
|
+
pid: null,
|
|
8836
|
+
appServerPid: null,
|
|
8837
|
+
lastVerifiedAt: Date.now(),
|
|
8838
|
+
lastStatus: "not-running",
|
|
8839
|
+
lastError: null
|
|
8840
|
+
};
|
|
8841
|
+
await patchManagedInstance(stopped2);
|
|
8842
|
+
return {
|
|
8843
|
+
status: "not-running",
|
|
8844
|
+
profileName,
|
|
8845
|
+
instance: managedInstanceToPayload(stopped2, false),
|
|
8846
|
+
reason: null
|
|
8847
|
+
};
|
|
8848
|
+
}
|
|
8849
|
+
const tree = [existing.pid, ...getDescendantPids(existing.pid, rows)].filter((pid, index, all) => all.indexOf(pid) === index).sort((a, b) => b - a);
|
|
8850
|
+
await signalPids(tree, "SIGTERM");
|
|
8851
|
+
const exited = await waitForMainPidExit(existing.pid, 5e3);
|
|
8852
|
+
if (!exited) {
|
|
8853
|
+
await signalPids(tree, "SIGKILL");
|
|
8854
|
+
await waitForMainPidExit(existing.pid, EXIT_WAIT_MS);
|
|
8855
|
+
}
|
|
8856
|
+
const stillRunning = (await readProcessRows()).some((row) => row.pid === existing.pid);
|
|
8857
|
+
const stopped = {
|
|
8858
|
+
...existing,
|
|
8859
|
+
pid: stillRunning ? existing.pid : null,
|
|
8860
|
+
appServerPid: stillRunning ? runtime.appServerPid : null,
|
|
8861
|
+
lastVerifiedAt: Date.now(),
|
|
8862
|
+
lastStatus: stillRunning ? "failed" : "stopped",
|
|
8863
|
+
lastError: stillRunning ? "stop-timeout" : null
|
|
8864
|
+
};
|
|
8865
|
+
await patchManagedInstance(stopped);
|
|
8866
|
+
return {
|
|
8867
|
+
status: stillRunning ? "failed" : "stopped",
|
|
8868
|
+
profileName,
|
|
8869
|
+
instance: managedInstanceToPayload(
|
|
8870
|
+
stopped,
|
|
8871
|
+
stillRunning,
|
|
8872
|
+
stopped.appServerPid,
|
|
8873
|
+
runtime.startedAt
|
|
8874
|
+
),
|
|
8875
|
+
reason: stillRunning ? "stop-timeout" : null
|
|
8876
|
+
};
|
|
8381
8877
|
});
|
|
8382
|
-
return {
|
|
8383
|
-
status,
|
|
8384
|
-
appPath: candidate.appPath,
|
|
8385
|
-
bundleId: candidate.bundleId,
|
|
8386
|
-
pid: launchedPid
|
|
8387
|
-
};
|
|
8388
8878
|
}
|
|
8389
|
-
function
|
|
8390
|
-
return
|
|
8879
|
+
function stopOfficialCodexObservedProfileInstance(profileName, pid, appServerPid) {
|
|
8880
|
+
return enqueueProfileAction(async () => {
|
|
8881
|
+
const { candidate } = await resolveCodexAppTarget();
|
|
8882
|
+
if (!candidate) {
|
|
8883
|
+
return {
|
|
8884
|
+
status: "not-running",
|
|
8885
|
+
profileName,
|
|
8886
|
+
instance: null,
|
|
8887
|
+
reason: "official-codex-app-not-found"
|
|
8888
|
+
};
|
|
8889
|
+
}
|
|
8890
|
+
const rows = await readProcessRows();
|
|
8891
|
+
const mainRow = rows.find((row) => row.pid === pid);
|
|
8892
|
+
if (!mainRow || !isMainProcessRow(mainRow, candidate)) {
|
|
8893
|
+
return {
|
|
8894
|
+
status: "not-running",
|
|
8895
|
+
profileName,
|
|
8896
|
+
instance: null,
|
|
8897
|
+
reason: null
|
|
8898
|
+
};
|
|
8899
|
+
}
|
|
8900
|
+
const runtimeAppServerPid = findAppServerPid(rows, pid) ?? appServerPid;
|
|
8901
|
+
const tree = [pid, ...getDescendantPids(pid, rows)].filter((entry, index, all) => all.indexOf(entry) === index).sort((a, b) => b - a);
|
|
8902
|
+
await signalPids(tree, "SIGTERM");
|
|
8903
|
+
const exited = await waitForMainPidExit(pid, 5e3);
|
|
8904
|
+
if (!exited) {
|
|
8905
|
+
await signalPids(tree, "SIGKILL");
|
|
8906
|
+
await waitForMainPidExit(pid, EXIT_WAIT_MS);
|
|
8907
|
+
}
|
|
8908
|
+
const stillRunning = (await readProcessRows()).some((row) => row.pid === pid);
|
|
8909
|
+
const instance = {
|
|
8910
|
+
profileName,
|
|
8911
|
+
profileKey: null,
|
|
8912
|
+
appPath: candidate.appPath,
|
|
8913
|
+
bundleId: candidate.bundleId,
|
|
8914
|
+
pid: stillRunning ? pid : null,
|
|
8915
|
+
appServerPid: stillRunning ? runtimeAppServerPid : null,
|
|
8916
|
+
running: stillRunning,
|
|
8917
|
+
startedAt: stillRunning ? toIso(mainRow.startedAt) : null,
|
|
8918
|
+
uptimeMs: stillRunning && typeof mainRow.startedAt === "number" ? Math.max(0, Date.now() - mainRow.startedAt) : null,
|
|
8919
|
+
launchedAt: null,
|
|
8920
|
+
lastVerifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8921
|
+
lastStatus: stillRunning ? "failed" : "stopped",
|
|
8922
|
+
lastError: stillRunning ? "stop-timeout" : null
|
|
8923
|
+
};
|
|
8924
|
+
return {
|
|
8925
|
+
status: stillRunning ? "failed" : "stopped",
|
|
8926
|
+
profileName,
|
|
8927
|
+
instance,
|
|
8928
|
+
reason: stillRunning ? "stop-timeout" : null
|
|
8929
|
+
};
|
|
8930
|
+
});
|
|
8391
8931
|
}
|
|
8392
|
-
function
|
|
8393
|
-
|
|
8394
|
-
|
|
8932
|
+
async function restartOfficialCodexProfileInstance(options) {
|
|
8933
|
+
const stopped = await stopOfficialCodexProfileInstance(options.profileName);
|
|
8934
|
+
if (stopped.status === "failed") {
|
|
8935
|
+
return stopped;
|
|
8395
8936
|
}
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
}
|
|
8401
|
-
restartPromise = null;
|
|
8402
|
-
});
|
|
8403
|
-
return restartPromise;
|
|
8937
|
+
const launched = await launchOfficialCodexProfileInstance(options);
|
|
8938
|
+
return {
|
|
8939
|
+
...launched,
|
|
8940
|
+
status: launched.status === "started" ? "restarted" : launched.status
|
|
8941
|
+
};
|
|
8404
8942
|
}
|
|
8405
8943
|
|
|
8406
8944
|
// src/commands/profile.ts
|
|
@@ -8852,33 +9390,190 @@ async function rememberOfficialCodexSwitch(profileKey) {
|
|
|
8852
9390
|
);
|
|
8853
9391
|
}
|
|
8854
9392
|
}
|
|
8855
|
-
function
|
|
9393
|
+
function formatOfficialCodexProfileAction(result) {
|
|
8856
9394
|
if (result.status === "restarted") {
|
|
8857
9395
|
return "Official Codex restarted.";
|
|
8858
9396
|
}
|
|
8859
9397
|
if (result.status === "started") {
|
|
8860
9398
|
return "Official Codex started.";
|
|
8861
9399
|
}
|
|
9400
|
+
if (result.status === "already-running") {
|
|
9401
|
+
return "Official Codex already running.";
|
|
9402
|
+
}
|
|
8862
9403
|
if (result.status === "failed") {
|
|
8863
9404
|
return `Official Codex restart failed: ${result.reason}.`;
|
|
8864
9405
|
}
|
|
8865
|
-
if (result.status === "skipped") {
|
|
9406
|
+
if (result.status === "skipped" || result.status === "not-running") {
|
|
8866
9407
|
return `Official Codex restart skipped: ${result.reason}.`;
|
|
8867
9408
|
}
|
|
8868
9409
|
return "Official Codex restart status unknown.";
|
|
8869
9410
|
}
|
|
9411
|
+
function findUnmanagedOfficialCodexRestartTarget(instances, profileName, profileKey) {
|
|
9412
|
+
const restartable = instances.unmanaged.filter((instance) => instance.pid);
|
|
9413
|
+
const matched = restartable.find((instance) => {
|
|
9414
|
+
if (profileKey && instance.profileKey) {
|
|
9415
|
+
return instance.profileKey === profileKey;
|
|
9416
|
+
}
|
|
9417
|
+
if (instance.profileKey) {
|
|
9418
|
+
return false;
|
|
9419
|
+
}
|
|
9420
|
+
return instance.profileName === profileName;
|
|
9421
|
+
}) ?? null;
|
|
9422
|
+
if (matched) {
|
|
9423
|
+
return { target: matched, ambiguous: false, unverified: false };
|
|
9424
|
+
}
|
|
9425
|
+
if (restartable.length > 1) {
|
|
9426
|
+
return { target: null, ambiguous: true, unverified: false };
|
|
9427
|
+
}
|
|
9428
|
+
return { target: null, ambiguous: false, unverified: restartable.length === 1 };
|
|
9429
|
+
}
|
|
9430
|
+
async function restartUnmanagedOfficialCodexWindow(profileName, launchOptions, target) {
|
|
9431
|
+
const stopped = await stopOfficialCodexObservedProfileInstance(
|
|
9432
|
+
target.profileName ?? profileName,
|
|
9433
|
+
target.pid,
|
|
9434
|
+
target.appServerPid
|
|
9435
|
+
);
|
|
9436
|
+
if (stopped.status === "failed") {
|
|
9437
|
+
return stopped;
|
|
9438
|
+
}
|
|
9439
|
+
const launched = await launchOfficialCodexProfileInstance(launchOptions);
|
|
9440
|
+
return {
|
|
9441
|
+
...launched,
|
|
9442
|
+
status: stopped.status === "stopped" && launched.status === "started" ? "restarted" : launched.status
|
|
9443
|
+
};
|
|
9444
|
+
}
|
|
9445
|
+
async function recordCliOfficialCodexRestartResult(profileName, profileKey, action) {
|
|
9446
|
+
const pid = action.status === "started" || action.status === "restarted" ? action.instance?.pid ?? null : null;
|
|
9447
|
+
const status = action.status === "started" || action.status === "restarted" ? action.status : action.status === "failed" ? "failed" : "skipped";
|
|
9448
|
+
const reason = action.status === "started" || action.status === "restarted" ? null : action.reason ?? action.status;
|
|
9449
|
+
try {
|
|
9450
|
+
await recordOfficialCodexRestartResult({
|
|
9451
|
+
status,
|
|
9452
|
+
reason,
|
|
9453
|
+
profileKey,
|
|
9454
|
+
pid
|
|
9455
|
+
});
|
|
9456
|
+
await recordOfficialCodexActivity({
|
|
9457
|
+
kind: "official-codex-restart",
|
|
9458
|
+
status,
|
|
9459
|
+
reason,
|
|
9460
|
+
targetProfileName: profileName,
|
|
9461
|
+
profileKey,
|
|
9462
|
+
phase: "cli",
|
|
9463
|
+
pid,
|
|
9464
|
+
restartRequested: true,
|
|
9465
|
+
restartResult: status
|
|
9466
|
+
});
|
|
9467
|
+
} catch (error) {
|
|
9468
|
+
console.warn(
|
|
9469
|
+
`Could not record official Codex restart state: ${error instanceof Error ? error.message : String(error)}`
|
|
9470
|
+
);
|
|
9471
|
+
}
|
|
9472
|
+
}
|
|
9473
|
+
async function recordCliOfficialCodexRestartSkipped(profileName, profileKey, reason) {
|
|
9474
|
+
await recordCliOfficialCodexRestartResult(profileName, profileKey, {
|
|
9475
|
+
status: "skipped",
|
|
9476
|
+
profileName,
|
|
9477
|
+
instance: null,
|
|
9478
|
+
reason
|
|
9479
|
+
});
|
|
9480
|
+
}
|
|
8870
9481
|
async function maybeRestartOfficialCodex(options) {
|
|
8871
9482
|
if (!options.enabled) {
|
|
8872
9483
|
return null;
|
|
8873
9484
|
}
|
|
8874
9485
|
try {
|
|
8875
|
-
const
|
|
8876
|
-
|
|
8877
|
-
|
|
8878
|
-
|
|
8879
|
-
|
|
8880
|
-
|
|
9486
|
+
const runtime = await options.manager.prepareProfileRuntime(options.profileName);
|
|
9487
|
+
const launchOptions = {
|
|
9488
|
+
profileName: options.profileName,
|
|
9489
|
+
profileKey: options.profileKey,
|
|
9490
|
+
profileHome: runtime.profileHome
|
|
9491
|
+
};
|
|
9492
|
+
const instances = await getOfficialCodexProfileInstances();
|
|
9493
|
+
if (instances.state === "unsupported") {
|
|
9494
|
+
await recordCliOfficialCodexRestartSkipped(
|
|
9495
|
+
options.profileName,
|
|
9496
|
+
options.profileKey,
|
|
9497
|
+
"unsupported-platform"
|
|
9498
|
+
);
|
|
9499
|
+
return "Official Codex restart skipped: unsupported-platform.";
|
|
9500
|
+
}
|
|
9501
|
+
if (instances.state === "not-found") {
|
|
9502
|
+
await recordCliOfficialCodexRestartSkipped(
|
|
9503
|
+
options.profileName,
|
|
9504
|
+
options.profileKey,
|
|
9505
|
+
"official-codex-app-not-found"
|
|
9506
|
+
);
|
|
9507
|
+
return "Official Codex restart skipped: official-codex-app-not-found.";
|
|
9508
|
+
}
|
|
9509
|
+
const managedRunning = instances.instances.some(
|
|
9510
|
+
(instance) => instance.profileName === options.profileName && instance.running
|
|
9511
|
+
);
|
|
9512
|
+
const unmanaged = findUnmanagedOfficialCodexRestartTarget(
|
|
9513
|
+
instances,
|
|
9514
|
+
options.profileName,
|
|
9515
|
+
options.profileKey
|
|
9516
|
+
);
|
|
9517
|
+
if (unmanaged.target && !managedRunning) {
|
|
9518
|
+
const action2 = await restartUnmanagedOfficialCodexWindow(
|
|
9519
|
+
options.profileName,
|
|
9520
|
+
launchOptions,
|
|
9521
|
+
unmanaged.target
|
|
9522
|
+
);
|
|
9523
|
+
await recordCliOfficialCodexRestartResult(
|
|
9524
|
+
options.profileName,
|
|
9525
|
+
options.profileKey,
|
|
9526
|
+
action2
|
|
9527
|
+
);
|
|
9528
|
+
return formatOfficialCodexProfileAction(action2);
|
|
9529
|
+
}
|
|
9530
|
+
if (unmanaged.ambiguous && !managedRunning) {
|
|
9531
|
+
await recordCliOfficialCodexRestartSkipped(
|
|
9532
|
+
options.profileName,
|
|
9533
|
+
options.profileKey,
|
|
9534
|
+
"ambiguous-official-codex-windows"
|
|
9535
|
+
);
|
|
9536
|
+
return "Official Codex restart skipped: ambiguous-official-codex-windows.";
|
|
9537
|
+
}
|
|
9538
|
+
if (unmanaged.unverified && !managedRunning) {
|
|
9539
|
+
await recordCliOfficialCodexRestartSkipped(
|
|
9540
|
+
options.profileName,
|
|
9541
|
+
options.profileKey,
|
|
9542
|
+
"unverified-official-codex-window"
|
|
9543
|
+
);
|
|
9544
|
+
return "Official Codex restart skipped: unverified-official-codex-window.";
|
|
9545
|
+
}
|
|
9546
|
+
if (options.launchIfNotRunning === false) {
|
|
9547
|
+
if (!managedRunning) {
|
|
9548
|
+
await recordCliOfficialCodexRestartSkipped(
|
|
9549
|
+
options.profileName,
|
|
9550
|
+
options.profileKey,
|
|
9551
|
+
"official-codex-app-not-running"
|
|
9552
|
+
);
|
|
9553
|
+
return "Official Codex restart skipped: official-codex-app-not-running.";
|
|
9554
|
+
}
|
|
9555
|
+
const action2 = await restartOfficialCodexProfileInstance(launchOptions);
|
|
9556
|
+
await recordCliOfficialCodexRestartResult(
|
|
9557
|
+
options.profileName,
|
|
9558
|
+
options.profileKey,
|
|
9559
|
+
action2
|
|
9560
|
+
);
|
|
9561
|
+
return formatOfficialCodexProfileAction(action2);
|
|
9562
|
+
}
|
|
9563
|
+
const action = await restartOfficialCodexProfileInstance(launchOptions);
|
|
9564
|
+
await recordCliOfficialCodexRestartResult(
|
|
9565
|
+
options.profileName,
|
|
9566
|
+
options.profileKey,
|
|
9567
|
+
action
|
|
9568
|
+
);
|
|
9569
|
+
return formatOfficialCodexProfileAction(action);
|
|
8881
9570
|
} catch (error) {
|
|
9571
|
+
await recordCliOfficialCodexRestartResult(options.profileName, options.profileKey, {
|
|
9572
|
+
status: "failed",
|
|
9573
|
+
profileName: options.profileName,
|
|
9574
|
+
instance: null,
|
|
9575
|
+
reason: error instanceof Error ? error.message : "restart-threw"
|
|
9576
|
+
});
|
|
8882
9577
|
return `Official Codex restart failed: ${error instanceof Error ? error.message : String(error)}.`;
|
|
8883
9578
|
}
|
|
8884
9579
|
}
|
|
@@ -9014,8 +9709,9 @@ async function runAutoRollPass(manager, options) {
|
|
|
9014
9709
|
const targetProfileKey = profileRateLimitKey(candidate.profile);
|
|
9015
9710
|
await rememberOfficialCodexSwitch(targetProfileKey);
|
|
9016
9711
|
const restartMessage = await maybeRestartOfficialCodex({
|
|
9712
|
+
manager,
|
|
9017
9713
|
enabled: options.restartOfficialCodex,
|
|
9018
|
-
|
|
9714
|
+
profileName: candidate.profile.name,
|
|
9019
9715
|
profileKey: targetProfileKey,
|
|
9020
9716
|
launchIfNotRunning: options.launchOfficialCodexWhenClosed === true
|
|
9021
9717
|
});
|
|
@@ -9129,8 +9825,9 @@ async function handleProfileCommand(args, version) {
|
|
|
9129
9825
|
console.log(`Switched to profile: ${name}`);
|
|
9130
9826
|
await rememberOfficialCodexSwitch(targetProfileKey);
|
|
9131
9827
|
const restartMessage = await maybeRestartOfficialCodex({
|
|
9828
|
+
manager,
|
|
9132
9829
|
enabled: restartOfficialCodex,
|
|
9133
|
-
|
|
9830
|
+
profileName: name,
|
|
9134
9831
|
profileKey: targetProfileKey
|
|
9135
9832
|
});
|
|
9136
9833
|
if (restartMessage) {
|
|
@@ -11503,7 +12200,7 @@ async function ensureCliStorageReady() {
|
|
|
11503
12200
|
}
|
|
11504
12201
|
|
|
11505
12202
|
// src/app/main.ts
|
|
11506
|
-
var VERSION = true ? "3.9.
|
|
12203
|
+
var VERSION = true ? "3.9.8" : "0.0.0";
|
|
11507
12204
|
async function runCli() {
|
|
11508
12205
|
const args = process.argv.slice(2);
|
|
11509
12206
|
if (args.length === 0) {
|