codex-team 0.0.10 → 0.0.11

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/main.cjs CHANGED
@@ -35,7 +35,9 @@ __webpack_require__.r(__webpack_exports__);
35
35
  __webpack_require__.d(__webpack_exports__, {
36
36
  runCli: ()=>runCli
37
37
  });
38
+ const promises_namespaceObject = require("node:fs/promises");
38
39
  const external_node_process_namespaceObject = require("node:process");
40
+ const external_node_path_namespaceObject = require("node:path");
39
41
  const external_dayjs_namespaceObject = require("dayjs");
40
42
  var external_dayjs_default = /*#__PURE__*/ __webpack_require__.n(external_dayjs_namespaceObject);
41
43
  const timezone_js_namespaceObject = require("dayjs/plugin/timezone.js");
@@ -43,10 +45,9 @@ var timezone_js_default = /*#__PURE__*/ __webpack_require__.n(timezone_js_namesp
43
45
  const utc_js_namespaceObject = require("dayjs/plugin/utc.js");
44
46
  var utc_js_default = /*#__PURE__*/ __webpack_require__.n(utc_js_namespaceObject);
45
47
  var package_namespaceObject = {
46
- rE: "0.0.10"
48
+ rE: "0.0.11"
47
49
  };
48
50
  const external_node_crypto_namespaceObject = require("node:crypto");
49
- const promises_namespaceObject = require("node:fs/promises");
50
51
  function isRecord(value) {
51
52
  return "object" == typeof value && null !== value && !Array.isArray(value);
52
53
  }
@@ -251,9 +252,6 @@ function decodeJwtPayload(token) {
251
252
  return parsed;
252
253
  }
253
254
  const external_node_os_namespaceObject = require("node:os");
254
- const external_node_path_namespaceObject = require("node:path");
255
- const external_node_child_process_namespaceObject = require("node:child_process");
256
- const external_node_util_namespaceObject = require("node:util");
257
255
  const DEFAULT_CHATGPT_BASE_URL = "https://chatgpt.com";
258
256
  const USER_AGENT = "codexm/0.1";
259
257
  const USAGE_FETCH_ATTEMPTS = 3;
@@ -542,7 +540,6 @@ async function fetchQuotaSnapshot(snapshot, options = {}) {
542
540
  };
543
541
  }
544
542
  }
545
- const execFile = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_namespaceObject.execFile);
546
543
  const DIRECTORY_MODE = 448;
547
544
  const FILE_MODE = 384;
548
545
  const SCHEMA_VERSION = 1;
@@ -615,25 +612,6 @@ function canAutoMigrateLegacyChatGPTMeta(meta, snapshot) {
615
612
  if (!snapshotUserId) return false;
616
613
  return meta.account_id === getSnapshotAccountId(snapshot);
617
614
  }
618
- async function detectRunningCodexProcesses() {
619
- try {
620
- const { stdout } = await execFile("ps", [
621
- "-Ao",
622
- "pid=,command="
623
- ]);
624
- const pids = [];
625
- for (const line of stdout.split("\n")){
626
- const match = line.trim().match(/^(\d+)\s+(.+)$/);
627
- if (!match) continue;
628
- const pid = Number(match[1]);
629
- const command = match[2];
630
- if (pid !== process.pid && /(^|\s|\/)codex(\s|$)/.test(command) && !command.includes("codex-team")) pids.push(pid);
631
- }
632
- return pids;
633
- } catch {
634
- return [];
635
- }
636
- }
637
615
  class AccountStore {
638
616
  paths;
639
617
  fetchImpl;
@@ -929,8 +907,6 @@ class AccountStore {
929
907
  last_switched_account: name,
930
908
  last_backup_path: backupPath
931
909
  });
932
- const runningCodexPids = await detectRunningCodexProcesses();
933
- if (runningCodexPids.length > 0) warnings.push(`Detected running codex processes (${runningCodexPids.join(", ")}). Existing sessions may still hold the previous login state.`);
934
910
  return {
935
911
  account: await this.readManagedAccount(name),
936
912
  warnings,
@@ -1104,6 +1080,252 @@ function createAccountStore(homeDir, options) {
1104
1080
  fetchImpl: options?.fetchImpl
1105
1081
  });
1106
1082
  }
1083
+ const external_node_child_process_namespaceObject = require("node:child_process");
1084
+ const external_node_util_namespaceObject = require("node:util");
1085
+ const execFile = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_namespaceObject.execFile);
1086
+ const DEFAULT_CODEX_REMOTE_DEBUGGING_PORT = 9223;
1087
+ const DEFAULT_CODEX_DESKTOP_STATE_PATH = (0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.homedir)(), ".codex-team", "desktop-state.json");
1088
+ const CODEX_BINARY_SUFFIX = "/Contents/MacOS/Codex";
1089
+ const CODEX_APP_NAME = "Codex";
1090
+ const CODEX_LOCAL_HOST_ID = "local";
1091
+ const CODEX_APP_SERVER_RESTART_EXPRESSION = 'window.electronBridge.sendMessageFromView({ type: "codex-app-server-restart", hostId: "local" })';
1092
+ const DEVTOOLS_REQUEST_TIMEOUT_MS = 5000;
1093
+ function codex_desktop_launch_isRecord(value) {
1094
+ return "object" == typeof value && null !== value && !Array.isArray(value);
1095
+ }
1096
+ function isNonEmptyString(value) {
1097
+ return "string" == typeof value && "" !== value.trim();
1098
+ }
1099
+ async function codex_desktop_launch_delay(ms) {
1100
+ await new Promise((resolve)=>setTimeout(resolve, ms));
1101
+ }
1102
+ async function pathExistsViaStat(execFileImpl, path) {
1103
+ try {
1104
+ await execFileImpl("stat", [
1105
+ "-f",
1106
+ "%N",
1107
+ path
1108
+ ]);
1109
+ return true;
1110
+ } catch {
1111
+ return false;
1112
+ }
1113
+ }
1114
+ function parseManagedState(raw) {
1115
+ if ("" === raw.trim()) return null;
1116
+ let parsed;
1117
+ try {
1118
+ parsed = JSON.parse(raw);
1119
+ } catch {
1120
+ return null;
1121
+ }
1122
+ if (!codex_desktop_launch_isRecord(parsed)) return null;
1123
+ const pid = parsed.pid;
1124
+ const appPath = parsed.app_path;
1125
+ const remoteDebuggingPort = parsed.remote_debugging_port;
1126
+ const managedByCodexm = parsed.managed_by_codexm;
1127
+ const startedAt = parsed.started_at;
1128
+ if ("number" != typeof pid || !Number.isInteger(pid) || pid <= 0 || !isNonEmptyString(appPath) || "number" != typeof remoteDebuggingPort || !Number.isInteger(remoteDebuggingPort) || remoteDebuggingPort <= 0 || true !== managedByCodexm || !isNonEmptyString(startedAt)) return null;
1129
+ return {
1130
+ pid,
1131
+ app_path: appPath,
1132
+ remote_debugging_port: remoteDebuggingPort,
1133
+ managed_by_codexm: true,
1134
+ started_at: startedAt
1135
+ };
1136
+ }
1137
+ async function ensureStateDirectory(statePath) {
1138
+ await (0, promises_namespaceObject.mkdir)((0, external_node_path_namespaceObject.dirname)(statePath), {
1139
+ recursive: true,
1140
+ mode: 448
1141
+ });
1142
+ }
1143
+ function createDefaultWebSocket(url) {
1144
+ return new WebSocket(url);
1145
+ }
1146
+ function isDevtoolsTarget(value) {
1147
+ return codex_desktop_launch_isRecord(value);
1148
+ }
1149
+ function isManagedDesktopProcess(runningApps, state) {
1150
+ const expectedBinaryPath = `${state.app_path}${CODEX_BINARY_SUFFIX}`;
1151
+ const expectedPort = `--remote-debugging-port=${state.remote_debugging_port}`;
1152
+ return runningApps.some((entry)=>entry.pid === state.pid && entry.command.includes(expectedBinaryPath) && entry.command.includes(expectedPort));
1153
+ }
1154
+ async function evaluateDevtoolsExpression(createWebSocketImpl, webSocketDebuggerUrl, expression) {
1155
+ const socket = createWebSocketImpl(webSocketDebuggerUrl);
1156
+ await new Promise((resolve, reject)=>{
1157
+ const requestId = 1;
1158
+ const timeout = setTimeout(()=>{
1159
+ cleanup();
1160
+ reject(new Error("Timed out waiting for Codex Desktop devtools response."));
1161
+ }, DEVTOOLS_REQUEST_TIMEOUT_MS);
1162
+ const cleanup = ()=>{
1163
+ clearTimeout(timeout);
1164
+ socket.onopen = null;
1165
+ socket.onmessage = null;
1166
+ socket.onerror = null;
1167
+ socket.onclose = null;
1168
+ socket.close();
1169
+ };
1170
+ socket.onopen = ()=>{
1171
+ socket.send(JSON.stringify({
1172
+ id: requestId,
1173
+ method: "Runtime.evaluate",
1174
+ params: {
1175
+ expression,
1176
+ awaitPromise: true
1177
+ }
1178
+ }));
1179
+ };
1180
+ socket.onmessage = (event)=>{
1181
+ if ("string" != typeof event.data) return;
1182
+ let payload;
1183
+ try {
1184
+ payload = JSON.parse(event.data);
1185
+ } catch {
1186
+ return;
1187
+ }
1188
+ if (!codex_desktop_launch_isRecord(payload) || payload.id !== requestId) return;
1189
+ if (codex_desktop_launch_isRecord(payload.error)) {
1190
+ cleanup();
1191
+ reject(new Error(String(payload.error.message ?? "Codex Desktop devtools request failed.")));
1192
+ return;
1193
+ }
1194
+ const result = codex_desktop_launch_isRecord(payload.result) ? payload.result : null;
1195
+ if (result && codex_desktop_launch_isRecord(result.exceptionDetails)) {
1196
+ cleanup();
1197
+ reject(new Error("Codex Desktop rejected the app-server restart request."));
1198
+ return;
1199
+ }
1200
+ cleanup();
1201
+ resolve();
1202
+ };
1203
+ socket.onerror = ()=>{
1204
+ cleanup();
1205
+ reject(new Error("Failed to communicate with Codex Desktop devtools."));
1206
+ };
1207
+ socket.onclose = ()=>{
1208
+ cleanup();
1209
+ reject(new Error("Codex Desktop devtools connection closed before replying."));
1210
+ };
1211
+ });
1212
+ }
1213
+ function createCodexDesktopLauncher(options = {}) {
1214
+ const execFileImpl = options.execFileImpl ?? execFile;
1215
+ const statePath = options.statePath ?? DEFAULT_CODEX_DESKTOP_STATE_PATH;
1216
+ const readFileImpl = options.readFileImpl ?? (async (path)=>(0, promises_namespaceObject.readFile)(path, "utf8"));
1217
+ const writeFileImpl = options.writeFileImpl ?? promises_namespaceObject.writeFile;
1218
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
1219
+ const createWebSocketImpl = options.createWebSocketImpl ?? createDefaultWebSocket;
1220
+ async function findInstalledApp() {
1221
+ const candidates = [
1222
+ "/Applications/Codex.app",
1223
+ (0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.homedir)(), "Applications", "Codex.app")
1224
+ ];
1225
+ for (const candidate of candidates)if (await pathExistsViaStat(execFileImpl, candidate)) return candidate;
1226
+ try {
1227
+ const { stdout } = await execFileImpl("mdfind", [
1228
+ 'kMDItemFSName == "Codex.app"'
1229
+ ]);
1230
+ for (const line of stdout.split("\n")){
1231
+ const candidate = line.trim();
1232
+ if ("" !== candidate) {
1233
+ if (await pathExistsViaStat(execFileImpl, candidate)) return candidate;
1234
+ }
1235
+ }
1236
+ } catch {}
1237
+ return null;
1238
+ }
1239
+ async function listRunningApps() {
1240
+ const { stdout } = await execFileImpl("ps", [
1241
+ "-Ao",
1242
+ "pid=,command="
1243
+ ]);
1244
+ const running = [];
1245
+ for (const line of stdout.split("\n")){
1246
+ const match = line.trim().match(/^(\d+)\s+(.+)$/);
1247
+ if (!match) continue;
1248
+ const pid = Number(match[1]);
1249
+ const command = match[2];
1250
+ if (pid !== process.pid && command.includes(CODEX_BINARY_SUFFIX)) running.push({
1251
+ pid,
1252
+ command
1253
+ });
1254
+ }
1255
+ return running;
1256
+ }
1257
+ async function quitRunningApps() {
1258
+ const running = await listRunningApps();
1259
+ if (0 === running.length) return;
1260
+ await execFileImpl("osascript", [
1261
+ "-e",
1262
+ `tell application "${CODEX_APP_NAME}" to quit`
1263
+ ]);
1264
+ for(let attempt = 0; attempt < 10; attempt += 1){
1265
+ const remaining = await listRunningApps();
1266
+ if (0 === remaining.length) return;
1267
+ await codex_desktop_launch_delay(300);
1268
+ }
1269
+ throw new Error("Timed out waiting for Codex Desktop to quit.");
1270
+ }
1271
+ async function launch(appPath) {
1272
+ await execFileImpl("open", [
1273
+ "-na",
1274
+ appPath,
1275
+ "--args",
1276
+ `--remote-debugging-port=${DEFAULT_CODEX_REMOTE_DEBUGGING_PORT}`
1277
+ ]);
1278
+ }
1279
+ async function readManagedState() {
1280
+ try {
1281
+ return parseManagedState(await readFileImpl(statePath));
1282
+ } catch {
1283
+ return null;
1284
+ }
1285
+ }
1286
+ async function writeManagedState(state) {
1287
+ await ensureStateDirectory(statePath);
1288
+ await writeFileImpl(statePath, `${JSON.stringify(state, null, 2)}\n`);
1289
+ }
1290
+ async function clearManagedState() {
1291
+ await ensureStateDirectory(statePath);
1292
+ await writeFileImpl(statePath, "");
1293
+ }
1294
+ async function isManagedDesktopRunning() {
1295
+ const state = await readManagedState();
1296
+ if (!state) return false;
1297
+ const runningApps = await listRunningApps();
1298
+ return isManagedDesktopProcess(runningApps, state);
1299
+ }
1300
+ async function restartManagedAppServer() {
1301
+ const state = await readManagedState();
1302
+ if (!state) return false;
1303
+ const runningApps = await listRunningApps();
1304
+ if (!isManagedDesktopProcess(runningApps, state)) return false;
1305
+ const response = await fetchImpl(`http://127.0.0.1:${state.remote_debugging_port}/json/list`);
1306
+ if (!response.ok) throw new Error(`Failed to query Codex Desktop devtools targets (HTTP ${response.status}).`);
1307
+ const targets = await response.json();
1308
+ if (!Array.isArray(targets)) throw new Error("Codex Desktop devtools target list was not an array.");
1309
+ const localTarget = targets.find((target)=>{
1310
+ if (!isDevtoolsTarget(target)) return false;
1311
+ return "page" === target.type && target.url === `app://-/index.html?hostId=${CODEX_LOCAL_HOST_ID}` && isNonEmptyString(target.webSocketDebuggerUrl);
1312
+ });
1313
+ if (!localTarget || !isNonEmptyString(localTarget.webSocketDebuggerUrl)) throw new Error("Could not find the local Codex Desktop devtools target.");
1314
+ await evaluateDevtoolsExpression(createWebSocketImpl, localTarget.webSocketDebuggerUrl, CODEX_APP_SERVER_RESTART_EXPRESSION);
1315
+ return true;
1316
+ }
1317
+ return {
1318
+ findInstalledApp,
1319
+ listRunningApps,
1320
+ quitRunningApps,
1321
+ launch,
1322
+ readManagedState,
1323
+ writeManagedState,
1324
+ clearManagedState,
1325
+ isManagedDesktopRunning,
1326
+ restartManagedAppServer
1327
+ };
1328
+ }
1107
1329
  external_dayjs_default().extend(utc_js_default());
1108
1330
  external_dayjs_default().extend(timezone_js_default());
1109
1331
  function parseArgs(argv) {
@@ -1147,12 +1369,29 @@ Usage:
1147
1369
  codexm update [--json]
1148
1370
  codexm switch <name> [--json]
1149
1371
  codexm switch --auto [--dry-run] [--json]
1372
+ codexm launch [name] [--json]
1150
1373
  codexm remove <name> [--yes] [--json]
1151
1374
  codexm rename <old> <new> [--json]
1152
1375
 
1153
1376
  Account names must match /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.
1154
1377
  `);
1155
1378
  }
1379
+ function stripManagedDesktopWarning(warnings) {
1380
+ return warnings.filter((warning)=>!warning.startsWith("Detected running codex processes (") || !warning.endsWith("Existing sessions may still hold the previous login state."));
1381
+ }
1382
+ async function refreshManagedDesktopAfterSwitch(warnings, desktopLauncher) {
1383
+ try {
1384
+ if (await desktopLauncher.restartManagedAppServer()) return;
1385
+ } catch (error) {
1386
+ warnings.push(`Failed to refresh the running codexm-managed Codex Desktop session: ${error.message}`);
1387
+ return;
1388
+ }
1389
+ try {
1390
+ const runningApps = await desktopLauncher.listRunningApps();
1391
+ if (0 === runningApps.length) return;
1392
+ warnings.push(`Detected running codex processes (${runningApps.map((app)=>app.pid).join(", ")}). Existing sessions may still hold the previous login state.`);
1393
+ } catch {}
1394
+ }
1156
1395
  function describeCurrentStatus(status) {
1157
1396
  const lines = [];
1158
1397
  if (status.exists) {
@@ -1347,6 +1586,67 @@ async function confirmRemoval(name, streams) {
1347
1586
  streams.stdin.on("data", onData);
1348
1587
  });
1349
1588
  }
1589
+ async function confirmDesktopRelaunch(streams) {
1590
+ if (!streams.stdin.isTTY) throw new Error("Refusing to relaunch Codex Desktop in a non-interactive terminal.");
1591
+ streams.stdout.write("Codex Desktop is already running. Close it and relaunch with the selected auth? [y/N] ");
1592
+ return await new Promise((resolve)=>{
1593
+ const cleanup = ()=>{
1594
+ streams.stdin.off("data", onData);
1595
+ streams.stdin.pause();
1596
+ };
1597
+ const onData = (buffer)=>{
1598
+ const answer = buffer.toString("utf8").trim().toLowerCase();
1599
+ cleanup();
1600
+ streams.stdout.write("\n");
1601
+ resolve("y" === answer || "yes" === answer);
1602
+ };
1603
+ streams.stdin.resume();
1604
+ streams.stdin.on("data", onData);
1605
+ });
1606
+ }
1607
+ async function sleep(ms) {
1608
+ await new Promise((resolve)=>setTimeout(resolve, ms));
1609
+ }
1610
+ async function main_pathExists(path) {
1611
+ try {
1612
+ await (0, promises_namespaceObject.stat)(path);
1613
+ return true;
1614
+ } catch (error) {
1615
+ const nodeError = error;
1616
+ if ("ENOENT" === nodeError.code) return false;
1617
+ throw error;
1618
+ }
1619
+ }
1620
+ function isRunningDesktopFromApp(app, appPath) {
1621
+ return app.command.includes(`${appPath}/Contents/MacOS/Codex`);
1622
+ }
1623
+ async function resolveManagedDesktopState(desktopLauncher, appPath, existingApps) {
1624
+ const existingPids = new Set(existingApps.map((app)=>app.pid));
1625
+ for(let attempt = 0; attempt < 10; attempt += 1){
1626
+ const runningApps = await desktopLauncher.listRunningApps();
1627
+ const launchedApp = runningApps.filter((app)=>isRunningDesktopFromApp(app, appPath) && !existingPids.has(app.pid)).sort((left, right)=>right.pid - left.pid)[0] ?? runningApps.filter((app)=>isRunningDesktopFromApp(app, appPath)).sort((left, right)=>right.pid - left.pid)[0] ?? null;
1628
+ if (launchedApp) return {
1629
+ pid: launchedApp.pid,
1630
+ app_path: appPath,
1631
+ remote_debugging_port: DEFAULT_CODEX_REMOTE_DEBUGGING_PORT,
1632
+ managed_by_codexm: true,
1633
+ started_at: new Date().toISOString()
1634
+ };
1635
+ await sleep(300);
1636
+ }
1637
+ return null;
1638
+ }
1639
+ async function restoreLaunchBackup(store, backupPath) {
1640
+ if (backupPath && await main_pathExists(backupPath)) await (0, promises_namespaceObject.copyFile)(backupPath, store.paths.currentAuthPath);
1641
+ else await (0, promises_namespaceObject.rm)(store.paths.currentAuthPath, {
1642
+ force: true
1643
+ });
1644
+ const configBackupPath = (0, external_node_path_namespaceObject.join)(store.paths.backupsDir, "last-active-config.toml");
1645
+ if (await main_pathExists(configBackupPath)) await (0, promises_namespaceObject.copyFile)(configBackupPath, store.paths.currentConfigPath);
1646
+ else await (0, promises_namespaceObject.rm)(store.paths.currentConfigPath, {
1647
+ force: true
1648
+ });
1649
+ }
1350
1650
  async function runCli(argv, options = {}) {
1351
1651
  const streams = {
1352
1652
  stdin: options.stdin ?? external_node_process_namespaceObject.stdin,
@@ -1354,6 +1654,7 @@ async function runCli(argv, options = {}) {
1354
1654
  stderr: options.stderr ?? external_node_process_namespaceObject.stderr
1355
1655
  };
1356
1656
  const store = options.store ?? createAccountStore();
1657
+ const desktopLauncher = options.desktopLauncher ?? createCodexDesktopLauncher();
1357
1658
  const parsed = parseArgs(argv);
1358
1659
  const json = parsed.flags.has("--json");
1359
1660
  try {
@@ -1486,6 +1787,8 @@ async function runCli(argv, options = {}) {
1486
1787
  }
1487
1788
  const result = await store.switchAccount(selected.name);
1488
1789
  for (const warning of warnings)result.warnings.push(warning);
1790
+ result.warnings = stripManagedDesktopWarning(result.warnings);
1791
+ await refreshManagedDesktopAfterSwitch(result.warnings, desktopLauncher);
1489
1792
  const payload = {
1490
1793
  ok: true,
1491
1794
  action: "switch",
@@ -1509,6 +1812,8 @@ async function runCli(argv, options = {}) {
1509
1812
  }
1510
1813
  if (!name) throw new Error("Usage: codexm switch <name>");
1511
1814
  const result = await store.switchAccount(name);
1815
+ result.warnings = stripManagedDesktopWarning(result.warnings);
1816
+ await refreshManagedDesktopAfterSwitch(result.warnings, desktopLauncher);
1512
1817
  let quota = null;
1513
1818
  try {
1514
1819
  await store.refreshQuotaForAccount(result.account.name);
@@ -1540,6 +1845,70 @@ async function runCli(argv, options = {}) {
1540
1845
  }
1541
1846
  return 0;
1542
1847
  }
1848
+ case "launch":
1849
+ {
1850
+ const name = parsed.positionals[0] ?? null;
1851
+ if (parsed.positionals.length > 1) throw new Error("Usage: codexm launch [name] [--json]");
1852
+ const warnings = [];
1853
+ const appPath = await desktopLauncher.findInstalledApp();
1854
+ if (!appPath) throw new Error("Codex Desktop not found at /Applications/Codex.app.");
1855
+ const runningApps = await desktopLauncher.listRunningApps();
1856
+ if (runningApps.length > 0) {
1857
+ const confirmed = await confirmDesktopRelaunch(streams);
1858
+ if (!confirmed) {
1859
+ if (json) writeJson(streams.stdout, {
1860
+ ok: false,
1861
+ action: "launch",
1862
+ cancelled: true
1863
+ });
1864
+ else streams.stdout.write("Aborted.\n");
1865
+ return 1;
1866
+ }
1867
+ await desktopLauncher.quitRunningApps();
1868
+ }
1869
+ let switchedAccount = null;
1870
+ let switchBackupPath = null;
1871
+ if (name) {
1872
+ const switchResult = await store.switchAccount(name);
1873
+ warnings.push(...stripManagedDesktopWarning(switchResult.warnings));
1874
+ switchedAccount = switchResult.account;
1875
+ switchBackupPath = switchResult.backup_path;
1876
+ }
1877
+ try {
1878
+ await desktopLauncher.launch(appPath);
1879
+ const managedState = await resolveManagedDesktopState(desktopLauncher, appPath, runningApps);
1880
+ if (!managedState) {
1881
+ await desktopLauncher.clearManagedState().catch(()=>void 0);
1882
+ throw new Error("Failed to confirm the newly launched Codex Desktop process for managed-session tracking.");
1883
+ }
1884
+ await desktopLauncher.writeManagedState(managedState);
1885
+ } catch (error) {
1886
+ if (switchedAccount) await restoreLaunchBackup(store, switchBackupPath).catch(()=>void 0);
1887
+ throw error;
1888
+ }
1889
+ if (json) writeJson(streams.stdout, {
1890
+ ok: true,
1891
+ action: "launch",
1892
+ account: switchedAccount ? {
1893
+ name: switchedAccount.name,
1894
+ account_id: switchedAccount.account_id,
1895
+ user_id: switchedAccount.user_id ?? null,
1896
+ identity: switchedAccount.identity,
1897
+ auth_mode: switchedAccount.auth_mode
1898
+ } : null,
1899
+ launched_with_current_auth: null === switchedAccount,
1900
+ app_path: appPath,
1901
+ relaunched: runningApps.length > 0,
1902
+ warnings
1903
+ });
1904
+ else {
1905
+ if (switchedAccount) streams.stdout.write(`Switched to "${switchedAccount.name}" (${maskAccountId(switchedAccount.identity)}).\n`);
1906
+ if (runningApps.length > 0) streams.stdout.write("Closed existing Codex Desktop instance and launched a new one.\n");
1907
+ streams.stdout.write(switchedAccount ? `Launched Codex Desktop with "${switchedAccount.name}" (${maskAccountId(switchedAccount.identity)}).\n` : "Launched Codex Desktop with current auth.\n");
1908
+ for (const warning of warnings)streams.stdout.write(`Warning: ${warning}\n`);
1909
+ }
1910
+ return 0;
1911
+ }
1543
1912
  case "remove":
1544
1913
  {
1545
1914
  const name = parsed.positionals[0];