codexuse-cli 5.0.1 → 5.0.2

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 CHANGED
@@ -2841,7 +2841,7 @@ function normalizeAppState(raw) {
2841
2841
  merged.officialCodex.activity = Array.isArray(merged.officialCodex.activity) ? merged.officialCodex.activity.filter((entry) => isRecord2(entry)).map((entry) => ({
2842
2842
  id: asString(entry.id) ?? "",
2843
2843
  at: asString(entry.at) ?? (/* @__PURE__ */ new Date(0)).toISOString(),
2844
- kind: entry.kind === "auto-roll-eval" || entry.kind === "profile-switch" || entry.kind === "auth-verified" || entry.kind === "official-codex-activation" || entry.kind === "official-codex-restart" || entry.kind === "reset-window-activation" || entry.kind === "low-remaining-alert" ? entry.kind : "auto-roll-eval",
2844
+ kind: entry.kind === "auto-roll-eval" || entry.kind === "profile-switch" || entry.kind === "auth-verified" || entry.kind === "official-codex-activation" || entry.kind === "official-codex-restart" || entry.kind === "official-codex-handoff" || entry.kind === "reset-window-activation" || entry.kind === "low-remaining-alert" ? entry.kind : "auto-roll-eval",
2845
2845
  status: asString(entry.status) ?? "unknown",
2846
2846
  reason: asString(entry.reason),
2847
2847
  decisionId: asString(entry.decisionId),
@@ -5293,7 +5293,7 @@ var ProfileManager = class {
5293
5293
  };
5294
5294
  }
5295
5295
  if (tokenAlert?.issue) {
5296
- const reason = typeof tokenAlert.reason === "string" && tokenAlert.reason.trim().length > 0 ? tokenAlert.reason.trim() : tokenAlert.issue === "refresh-redeemed" ? REFRESH_TOKEN_REDEEMED_REASON : "Authentication needs attention. Re-login this profile.";
5296
+ const reason = typeof tokenAlert.reason === "string" && tokenAlert.reason.trim().length > 0 ? tokenAlert.reason.trim() : tokenAlert.issue === "refresh-redeemed" ? REFRESH_TOKEN_REDEEMED_REASON : "Authentication issue. Fix sign-in.";
5297
5297
  const issue = tokenAlert.issue ?? "auth-missing";
5298
5298
  return {
5299
5299
  state: "invalid",
@@ -8584,6 +8584,35 @@ function sanitizeOfficialCodexOverlayActionTargetsForRenderer(targets) {
8584
8584
  }
8585
8585
  return sanitized;
8586
8586
  }
8587
+ function normalizeOverlayAutoRollDecision(value) {
8588
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
8589
+ return null;
8590
+ }
8591
+ const record = value;
8592
+ const id = typeof record.id === "string" ? record.id.trim().slice(0, 96) : "";
8593
+ if (!id) {
8594
+ return null;
8595
+ }
8596
+ const sourceProfileName = typeof record.sourceProfileName === "string" ? record.sourceProfileName.trim().slice(0, 160) : "";
8597
+ const targetProfileName = typeof record.targetProfileName === "string" ? record.targetProfileName.trim().slice(0, 160) : "";
8598
+ const sourceProfileLabel = typeof record.sourceProfileLabel === "string" && record.sourceProfileLabel.trim() ? record.sourceProfileLabel.trim().slice(0, 80) : "Current account";
8599
+ const targetProfileLabel = typeof record.targetProfileLabel === "string" && record.targetProfileLabel.trim() ? record.targetProfileLabel.trim().slice(0, 80) : "Next account";
8600
+ const remainingPercent = typeof record.remainingPercent === "number" && Number.isFinite(record.remainingPercent) ? Math.max(0, Math.min(100, record.remainingPercent)) : null;
8601
+ const switchRemainingThreshold = typeof record.switchRemainingThreshold === "number" && Number.isFinite(record.switchRemainingThreshold) ? Math.max(0, Math.min(100, record.switchRemainingThreshold)) : 5;
8602
+ const expiresAt = typeof record.expiresAt === "string" && record.expiresAt.trim() ? record.expiresAt.trim() : null;
8603
+ const createdAt = typeof record.createdAt === "string" && record.createdAt.trim() ? record.createdAt.trim() : (/* @__PURE__ */ new Date()).toISOString();
8604
+ return {
8605
+ id,
8606
+ sourceProfileName,
8607
+ sourceProfileLabel,
8608
+ targetProfileName,
8609
+ targetProfileLabel,
8610
+ remainingPercent,
8611
+ switchRemainingThreshold,
8612
+ expiresAt,
8613
+ createdAt
8614
+ };
8615
+ }
8587
8616
  function bridgeCapabilitiesUnavailable() {
8588
8617
  return {
8589
8618
  health: false,
@@ -9095,7 +9124,7 @@ function buildInCodexStatusOverlayCssRules() {
9095
9124
  const id = "#codexuse-status-pill";
9096
9125
  const chip = "#codexuse-handoff-chip";
9097
9126
  const picker = "#codexuse-handoff-picker";
9098
- const palette = "--cu-surface:color-mix(in srgb,var(--cu-bg,#202123) 94%,var(--cu-fg,#ececf1) 6%);--cu-popover:color-mix(in srgb,var(--cu-bg,#202123) 97%,var(--cu-fg,#ececf1) 3%);--cu-border:color-mix(in srgb,var(--cu-fg,#ececf1) 16%,transparent);--cu-muted:color-mix(in srgb,var(--cu-fg,#ececf1) 60%,var(--cu-bg,#202123) 40%);--cu-hover:color-mix(in srgb,var(--cu-fg,#ececf1) 7%,var(--cu-bg,#202123) 93%);";
9127
+ const palette = "--cu-surface:color-mix(in srgb,var(--cu-bg,#202123) 94%,var(--cu-fg,#ececf1) 6%);--cu-popover:color-mix(in srgb,var(--cu-bg,#202123) 97%,var(--cu-fg,#ececf1) 3%);--cu-border:color-mix(in srgb,var(--cu-fg,#ececf1) 16%,transparent);--cu-muted:color-mix(in srgb,var(--cu-fg,#ececf1) 60%,var(--cu-bg,#202123) 40%);--cu-hover:color-mix(in srgb,var(--cu-fg,#ececf1) 7%,var(--cu-bg,#202123) 93%);--cu-accent:#5e9eff;";
9099
9128
  return [
9100
9129
  `${id}{${palette}position:fixed;right:16px;bottom:16px;z-index:2147483001;border:1px solid var(--cu-border);border-radius:12px;background:var(--cu-surface);color:var(--cu-fg,#ececf1);font-family:inherit;font-size:12px;line-height:1.4;box-shadow:0 8px 28px rgba(0,0,0,.28);pointer-events:auto;user-select:none;-webkit-user-select:none;}`,
9101
9130
  `${id}[data-codexuse-qol-status='degraded']{border-color:color-mix(in srgb,#d97706 55%,var(--cu-border));}`,
@@ -9123,16 +9152,30 @@ function buildInCodexStatusOverlayCssRules() {
9123
9152
  `${id}[data-open='true'] .codexuse-action-menu{display:flex;flex-direction:column;gap:2px;}`,
9124
9153
  `${id} .codexuse-target-list{display:flex;max-height:210px;flex-direction:column;gap:2px;overflow-y:auto;}`,
9125
9154
  `${id} .codexuse-target-row{display:flex;width:100%;align-items:center;justify-content:space-between;gap:10px;padding:7px 9px;border-radius:8px;cursor:pointer;text-align:left;transition:background 120ms ease-out;}`,
9155
+ `${id} .codexuse-target-row[data-current='true']{cursor:default;opacity:.82;}`,
9126
9156
  `${id} .codexuse-target-row:hover{background:var(--cu-hover);}`,
9157
+ `${id} .codexuse-target-row[data-current='true']:hover{background:transparent;}`,
9127
9158
  `${id} .codexuse-target-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500;}`,
9128
9159
  `${id} .codexuse-target-detail{flex-shrink:0;color:var(--cu-muted);font-size:10px;white-space:nowrap;}`,
9129
9160
  `${id} .codexuse-action-empty{margin:0;padding:7px 9px;color:var(--cu-muted);font-size:11px;}`,
9161
+ `${id} .codexuse-decision{display:flex;flex-direction:column;gap:7px;margin:2px 2px 5px;padding:9px;border-radius:10px;border:1px solid color-mix(in srgb,var(--cu-accent) 34%,var(--cu-border));background:color-mix(in srgb,var(--cu-accent) 10%,var(--cu-popover));}`,
9162
+ `${id} .codexuse-decision-title{font-size:11px;font-weight:700;color:var(--cu-fg,#ececf1);}`,
9163
+ `${id} .codexuse-decision-detail{font-size:10px;color:var(--cu-muted);}`,
9164
+ `${id} .codexuse-decision-actions{display:flex;gap:6px;}`,
9165
+ `${id} .codexuse-decision-actions button{flex:1;border-radius:7px;padding:5px 7px;cursor:pointer;font-size:10px;font-weight:700;}`,
9166
+ `${id} .codexuse-decision-primary{background:var(--cu-accent)!important;color:#fff!important;}`,
9167
+ `${id} .codexuse-intro{margin:2px 2px 5px;padding:8px 9px;border-radius:10px;border:1px solid color-mix(in srgb,var(--cu-accent) 30%,var(--cu-border));background:color-mix(in srgb,var(--cu-accent) 8%,var(--cu-popover));}`,
9168
+ `${id} .codexuse-intro-title{font-size:11px;font-weight:700;color:var(--cu-fg,#ececf1);}`,
9169
+ `${id} .codexuse-intro-detail{margin-top:2px;font-size:10px;color:var(--cu-muted);}`,
9170
+ `${id} .codexuse-decision-secondary{border:1px solid var(--cu-border)!important;color:var(--cu-muted)!important;}`,
9130
9171
  `${id} .codexuse-menu-divider{height:1px;margin:4px 2px;background:var(--cu-border);}`,
9172
+ `${id} .codexuse-command-row{display:flex;width:100%;align-items:center;justify-content:space-between;gap:10px;padding:6px 9px;border-radius:8px;cursor:pointer;text-align:left;transition:background 120ms ease-out;}`,
9173
+ `${id} .codexuse-command-row:hover{background:var(--cu-hover);}`,
9131
9174
  `${id} .codexuse-setting-row{display:flex;width:100%;align-items:center;justify-content:space-between;gap:10px;padding:6px 9px;border-radius:8px;cursor:pointer;text-align:left;transition:background 120ms ease-out;}`,
9132
9175
  `${id} .codexuse-setting-row:hover{background:var(--cu-hover);}`,
9133
9176
  `${id} .codexuse-toggle{position:relative;flex-shrink:0;width:26px;height:15px;border-radius:999px;background:var(--cu-border);transition:background 120ms ease-out;}`,
9134
9177
  `${id} .codexuse-toggle::after{content:'';position:absolute;left:2px;top:2px;width:11px;height:11px;border-radius:50%;background:var(--cu-fg,#ececf1);transition:transform 120ms ease-out;}`,
9135
- `${id} .codexuse-setting-row[data-on='true'] .codexuse-toggle{background:color-mix(in srgb,#10a37f 75%,var(--cu-bg,#202123));}`,
9178
+ `${id} .codexuse-setting-row[data-on='true'] .codexuse-toggle{background:var(--cu-accent);}`,
9136
9179
  `${id} .codexuse-setting-row[data-on='true'] .codexuse-toggle::after{transform:translateX(11px);}`,
9137
9180
  `${id} .codexuse-menu-footer{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:4px 9px 2px;}`,
9138
9181
  `${id} .codexuse-action-status{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--cu-muted);font-size:10px;}`,
@@ -9163,6 +9206,12 @@ function createBridgeApplyQolExpression(settings) {
9163
9206
  const conversationTimelineEnabled = ${JSON.stringify(settings.conversationTimelineEnabled === true)};
9164
9207
  const inCodexStatusEnabled = ${JSON.stringify(settings.inCodexStatusEnabled === true)};
9165
9208
  const handoffChipEnabled = ${JSON.stringify(settings.handoffChipEnabled !== false)};
9209
+ let currentCanonicalSyncLine = ${JSON.stringify(
9210
+ typeof settings.canonicalSyncLine === "string" ? settings.canonicalSyncLine.trim().slice(0, 40) : ""
9211
+ )};
9212
+ let currentAutoRollDecision = ${JSON.stringify(
9213
+ normalizeOverlayAutoRollDecision(settings.autoRollDecision)
9214
+ )};
9166
9215
  const actionTargets = ${JSON.stringify(
9167
9216
  sanitizeOfficialCodexOverlayActionTargetsForRenderer(
9168
9217
  settings.actionTargets
@@ -9244,11 +9293,11 @@ function createBridgeApplyQolExpression(settings) {
9244
9293
  wideViewEnabled ? ".thread-scroll-container > div:has([class*='max-w-(--thread-content-max-width)']){transform:none!important;}" : "",
9245
9294
  conversationTimelineEnabled ? "." + timelineClass + "{position:fixed;right:10px;top:18%;bottom:18%;z-index:2147483000;width:22px;pointer-events:none;}" : "",
9246
9295
  conversationTimelineEnabled ? "." + timelineClass + "::before{content:'';position:absolute;left:10px;top:0;bottom:0;width:2px;border-radius:2px;background:rgba(127,127,127,.35);}" : "",
9247
- conversationTimelineEnabled ? "." + timelineClass + " button{position:absolute;left:4px;width:14px;height:14px;border-radius:50%;border:1px solid rgba(16,163,127,.7);background:#10a37f;box-shadow:0 2px 10px rgba(0,0,0,.24);pointer-events:auto;padding:0;}" : "",
9296
+ conversationTimelineEnabled ? "." + timelineClass + " button{position:absolute;left:4px;width:14px;height:14px;border-radius:50%;border:1px solid rgba(94,158,255,.7);background:#5e9eff;box-shadow:0 2px 10px rgba(0,0,0,.24);pointer-events:auto;padding:0;}" : "",
9248
9297
  conversationTimelineEnabled ? "." + timelineClass + " button span{position:absolute;right:18px;top:50%;transform:translateY(-50%);display:none;max-width:220px;border-radius:6px;background:rgba(24,24,27,.96);color:white;font:12px/1.35 system-ui,sans-serif;padding:5px 7px;text-align:left;white-space:normal;}" : "",
9249
9298
  conversationTimelineEnabled ? "." + timelineClass + " button:hover span,." + timelineClass + " button:focus-visible span{display:block;}" : "",
9250
9299
  conversationTimelineEnabled ? "." + timelineTargetClass + "{animation:codexuse-conversation-timeline-pulse 1.2s ease-out;}" : "",
9251
- conversationTimelineEnabled ? "@keyframes codexuse-conversation-timeline-pulse{0%{box-shadow:0 0 0 0 rgba(16,163,127,.45);}100%{box-shadow:0 0 0 14px rgba(16,163,127,0);}}" : "",
9300
+ conversationTimelineEnabled ? "@keyframes codexuse-conversation-timeline-pulse{0%{box-shadow:0 0 0 0 rgba(94,158,255,.45);}100%{box-shadow:0 0 0 14px rgba(94,158,255,0);}}" : "",
9252
9301
  ...(inCodexStatusEnabled ? ${JSON.stringify(buildInCodexStatusOverlayCssRules())} : [])
9253
9302
  ].filter(Boolean).join("\\n");
9254
9303
  } else {
@@ -9623,7 +9672,7 @@ function createBridgeApplyQolExpression(settings) {
9623
9672
  document.querySelector("[data-app-action-sidebar-thread-id][aria-current='page']");
9624
9673
  return row?.getAttribute("data-app-action-sidebar-thread-id") || null;
9625
9674
  };
9626
- const requestAction = (action, target = null, setting = null) => {
9675
+ const requestAction = (action, target = null, setting = null, decisionId = null) => {
9627
9676
  const bridge = window.__codexuseBridgeV2 && typeof window.__codexuseBridgeV2 === "object"
9628
9677
  ? window.__codexuseBridgeV2
9629
9678
  : {};
@@ -9640,6 +9689,7 @@ function createBridgeApplyQolExpression(settings) {
9640
9689
  id: "codexuse-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 8),
9641
9690
  action,
9642
9691
  targetProfileKeyHash: target && typeof target.profileKeyHash === "string" ? target.profileKeyHash : null,
9692
+ autoRollDecisionId: typeof decisionId === "string" && decisionId ? decisionId : null,
9643
9693
  settingKey: setting && typeof setting.key === "string" ? setting.key : null,
9644
9694
  settingValue: setting && typeof setting.value === "boolean" ? setting.value : null,
9645
9695
  route: route(),
@@ -9652,6 +9702,9 @@ function createBridgeApplyQolExpression(settings) {
9652
9702
  setOverlayMessage(
9653
9703
  action === "switch" ? "Switching\u2026"
9654
9704
  : action === "continue" ? "Handing off\u2026"
9705
+ : action === "auto-roll-accept" ? "Switching\u2026"
9706
+ : action === "auto-roll-cancel" ? "Staying here"
9707
+ : action === "restart" ? "Waiting to restart; keeping draft\u2026"
9655
9708
  : action === "setting" ? "Saving\u2026"
9656
9709
  : "Requested"
9657
9710
  );
@@ -9692,6 +9745,12 @@ function createBridgeApplyQolExpression(settings) {
9692
9745
  return;
9693
9746
  }
9694
9747
  setTargets(result.actionTargets);
9748
+ if (typeof result.canonicalSyncLine === "string") {
9749
+ currentCanonicalSyncLine = result.canonicalSyncLine.trim().slice(0, 40);
9750
+ }
9751
+ currentAutoRollDecision = result.autoRollDecision && typeof result.autoRollDecision === "object"
9752
+ ? result.autoRollDecision
9753
+ : null;
9695
9754
  renderStatus();
9696
9755
  if (typeof onUpdated === "function") onUpdated();
9697
9756
  }).catch(() => {
@@ -9731,7 +9790,7 @@ function createBridgeApplyQolExpression(settings) {
9731
9790
  applyStoredOverlayPosition(pill);
9732
9791
  return true;
9733
9792
  }
9734
- pill.dataset.open = wasOpen || shouldShowIntro ? "true" : "false";
9793
+ pill.dataset.open = currentAutoRollDecision || wasOpen || shouldShowIntro ? "true" : "false";
9735
9794
  state.overlayMenuOpen = pill.dataset.open === "true";
9736
9795
  const trigger = document.createElement("button");
9737
9796
  trigger.type = "button";
@@ -9742,7 +9801,7 @@ function createBridgeApplyQolExpression(settings) {
9742
9801
  triggerLabel.setAttribute("data-codexuse-status-label", "true");
9743
9802
  triggerLabel.textContent = activeTarget?.label?.trim() || "CodexUse";
9744
9803
  trigger.appendChild(triggerLabel);
9745
- const triggerDetailText = activeTarget?.detail?.trim() || "";
9804
+ const triggerDetailText = currentCanonicalSyncLine || activeTarget?.detail?.trim() || "";
9746
9805
  const triggerDetail = document.createElement("span");
9747
9806
  triggerDetail.className = "codexuse-status-chip";
9748
9807
  triggerDetail.setAttribute("data-codexuse-status-detail", "true");
@@ -9751,7 +9810,7 @@ function createBridgeApplyQolExpression(settings) {
9751
9810
  const triggerChevron = document.createElement("span");
9752
9811
  triggerChevron.className = "codexuse-status-chevron";
9753
9812
  triggerChevron.setAttribute("aria-hidden", "true");
9754
- triggerChevron.innerHTML = "▾";
9813
+ triggerChevron.innerHTML = '<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>';
9755
9814
  trigger.appendChild(triggerChevron);
9756
9815
  // Drag-to-move on the trigger; a real click (under 4px travel) toggles.
9757
9816
  let dragMoved = false;
@@ -9795,9 +9854,107 @@ function createBridgeApplyQolExpression(settings) {
9795
9854
  }, true);
9796
9855
  const menu = document.createElement("div");
9797
9856
  menu.className = "codexuse-action-menu";
9798
- if (switchable.length > 0) {
9857
+ if (shouldShowIntro) {
9858
+ const intro = document.createElement("div");
9859
+ intro.className = "codexuse-intro";
9860
+ const introTitle = document.createElement("div");
9861
+ introTitle.className = "codexuse-intro-title";
9862
+ introTitle.textContent = "CodexUse controls";
9863
+ const introDetail = document.createElement("div");
9864
+ introDetail.className = "codexuse-intro-detail";
9865
+ introDetail.textContent = "Switch accounts, restart Codex, and confirm auto-roll from here. Drag to move, or Hide to dismiss.";
9866
+ intro.appendChild(introTitle);
9867
+ intro.appendChild(introDetail);
9868
+ menu.appendChild(intro);
9869
+ }
9870
+ if (currentAutoRollDecision) {
9871
+ const decision = currentAutoRollDecision;
9872
+ const panel = document.createElement("div");
9873
+ panel.className = "codexuse-decision";
9874
+ const title = document.createElement("div");
9875
+ title.className = "codexuse-decision-title";
9876
+ const remaining = typeof decision.remainingPercent === "number"
9877
+ ? Math.max(0, Math.min(100, Math.round(decision.remainingPercent * 10) / 10)) + "% left"
9878
+ : "low quota";
9879
+ title.textContent = decision.sourceProfileLabel + " at " + remaining + " \u2192 switch to " + decision.targetProfileLabel + "?";
9880
+ panel.appendChild(title);
9881
+ const detail = document.createElement("div");
9882
+ detail.className = "codexuse-decision-detail";
9883
+ const updateDecisionCountdown = () => {
9884
+ const expiresAtMs = typeof decision.expiresAt === "string"
9885
+ ? Date.parse(decision.expiresAt)
9886
+ : NaN;
9887
+ if (!Number.isFinite(expiresAtMs)) {
9888
+ detail.textContent = "Auto-roll is waiting for this decision.";
9889
+ return;
9890
+ }
9891
+ const secondsLeft = Math.max(0, Math.ceil((expiresAtMs - Date.now()) / 1000));
9892
+ detail.textContent = secondsLeft > 0
9893
+ ? "Auto-switches in " + secondsLeft + "s unless you stay."
9894
+ : "Auto-roll is switching now.";
9895
+ };
9896
+ updateDecisionCountdown();
9897
+ const countdownTimer = window.setInterval(() => {
9898
+ if (!document.body.contains(detail)) {
9899
+ window.clearInterval(countdownTimer);
9900
+ return;
9901
+ }
9902
+ updateDecisionCountdown();
9903
+ }, 500);
9904
+ panel.appendChild(detail);
9905
+ const actions = document.createElement("div");
9906
+ actions.className = "codexuse-decision-actions";
9907
+ const accept = document.createElement("button");
9908
+ accept.type = "button";
9909
+ accept.className = "codexuse-decision-primary";
9910
+ accept.textContent = "Switch now";
9911
+ accept.addEventListener("click", (event) => {
9912
+ event.preventDefault();
9913
+ event.stopPropagation();
9914
+ currentAutoRollDecision = null;
9915
+ requestAction("auto-roll-accept", null, null, decision.id);
9916
+ renderStatus();
9917
+ }, true);
9918
+ const cancel = document.createElement("button");
9919
+ cancel.type = "button";
9920
+ cancel.className = "codexuse-decision-secondary";
9921
+ cancel.textContent = "Stay";
9922
+ cancel.addEventListener("click", (event) => {
9923
+ event.preventDefault();
9924
+ event.stopPropagation();
9925
+ currentAutoRollDecision = null;
9926
+ requestAction("auto-roll-cancel", null, null, decision.id);
9927
+ renderStatus();
9928
+ }, true);
9929
+ actions.appendChild(accept);
9930
+ actions.appendChild(cancel);
9931
+ panel.appendChild(actions);
9932
+ menu.appendChild(panel);
9933
+ }
9934
+ if (activeTarget || switchable.length > 0) {
9799
9935
  const list = document.createElement("div");
9800
9936
  list.className = "codexuse-target-list";
9937
+ if (activeTarget) {
9938
+ const row = document.createElement("div");
9939
+ row.className = "codexuse-target-row";
9940
+ row.dataset.current = "true";
9941
+ const label = document.createElement("span");
9942
+ label.className = "codexuse-target-label";
9943
+ label.textContent =
9944
+ typeof activeTarget.label === "string" && activeTarget.label.trim()
9945
+ ? activeTarget.label.trim()
9946
+ : "Account";
9947
+ row.appendChild(label);
9948
+ const detail = document.createElement("span");
9949
+ detail.className = "codexuse-target-detail";
9950
+ const detailText =
9951
+ typeof activeTarget.detail === "string" && activeTarget.detail.trim()
9952
+ ? "Current \xB7 " + activeTarget.detail.trim()
9953
+ : "Current";
9954
+ detail.textContent = detailText;
9955
+ row.appendChild(detail);
9956
+ list.appendChild(row);
9957
+ }
9801
9958
  switchable.forEach((target) => {
9802
9959
  const row = document.createElement("button");
9803
9960
  row.type = "button";
@@ -9829,38 +9986,28 @@ function createBridgeApplyQolExpression(settings) {
9829
9986
  empty.textContent = "No other account ready";
9830
9987
  menu.appendChild(empty);
9831
9988
  }
9832
- const settingToggles = [
9833
- ["Auto-roll", "autoRoll", overlayAutoRollEnabled],
9834
- ["Wide view", "wideView", wideViewEnabled],
9835
- ["Scroll memory", "scrollRestore", scrollRestoreEnabled],
9836
- ["Timeline", "conversationTimeline", conversationTimelineEnabled],
9837
- ["Hand off", "handoffChip", handoffChipEnabled]
9838
- ].filter((entry) => typeof entry[2] === "boolean");
9839
- if (settingToggles.length > 0) {
9840
- const divider = document.createElement("div");
9841
- divider.className = "codexuse-menu-divider";
9842
- menu.appendChild(divider);
9843
- settingToggles.forEach(([labelText, key, value]) => {
9844
- const row = document.createElement("button");
9845
- row.type = "button";
9846
- row.className = "codexuse-setting-row";
9847
- row.dataset.on = value === true ? "true" : "false";
9848
- const label = document.createElement("span");
9849
- label.textContent = labelText;
9850
- const toggle = document.createElement("span");
9851
- toggle.className = "codexuse-toggle";
9852
- row.appendChild(label);
9853
- row.appendChild(toggle);
9854
- row.addEventListener("click", (event) => {
9855
- event.preventDefault();
9856
- event.stopPropagation();
9857
- const next = row.dataset.on !== "true";
9858
- row.dataset.on = next ? "true" : "false";
9859
- requestAction("setting", null, { key, value: next });
9860
- }, true);
9861
- menu.appendChild(row);
9862
- });
9863
- }
9989
+ const restartRow = document.createElement("button");
9990
+ restartRow.type = "button";
9991
+ restartRow.className = "codexuse-command-row";
9992
+ restartRow.title = "Restart Official Codex";
9993
+ const restartLabel = document.createElement("span");
9994
+ restartLabel.textContent = "Restart Codex";
9995
+ restartRow.appendChild(restartLabel);
9996
+ const restartDetail = document.createElement("span");
9997
+ restartDetail.className = "codexuse-target-detail";
9998
+ restartDetail.textContent = "Keeps draft when possible";
9999
+ restartRow.appendChild(restartDetail);
10000
+ restartRow.addEventListener("click", (event) => {
10001
+ event.preventDefault();
10002
+ event.stopPropagation();
10003
+ pill.dataset.open = "false";
10004
+ state.overlayMenuOpen = false;
10005
+ requestAction("restart");
10006
+ }, true);
10007
+ menu.appendChild(restartRow);
10008
+ // In-app tweaks (auto-roll, wide view, scroll restore, timeline, hand-off)
10009
+ // are configured in the CodexUse cockpit's Advanced section, not here \u2014
10010
+ // the overlay stays focused on account status, switching, and recovery.
9864
10011
  const footer = document.createElement("div");
9865
10012
  footer.className = "codexuse-menu-footer";
9866
10013
  const actionStatus = document.createElement("span");
@@ -9941,7 +10088,7 @@ function createBridgeApplyQolExpression(settings) {
9941
10088
  chip.type = "button";
9942
10089
  chip.id = handoffId;
9943
10090
  applyOverlayTheme(chip);
9944
- chip.textContent = "Hand off";
10091
+ chip.textContent = "Continue there";
9945
10092
  chip.title = "Continue this thread on another account";
9946
10093
  chip.style.top = chipTop + "px";
9947
10094
  chip.style.right = chipRight + "px";
@@ -11380,6 +11527,8 @@ function normalizeOfficialCodexQolSettings2(value) {
11380
11527
  inCodexStatusEnabled: value.inCodexStatusEnabled !== false,
11381
11528
  handoffChipEnabled: value.handoffChipEnabled !== false,
11382
11529
  actionTargets,
11530
+ canonicalSyncLine: typeof value.canonicalSyncLine === "string" && value.canonicalSyncLine.trim() ? value.canonicalSyncLine.trim().slice(0, 40) : null,
11531
+ autoRollDecision: normalizeOverlayAutoRollDecision(value.autoRollDecision),
11383
11532
  overlayAutoRollEnabled: typeof value.overlayAutoRollEnabled === "boolean" ? value.overlayAutoRollEnabled : null
11384
11533
  };
11385
11534
  }
@@ -12203,7 +12352,7 @@ function startOfficialCodexBridgeActionListener(args) {
12203
12352
  if (!id || !rememberBridgeActionId(id)) {
12204
12353
  return;
12205
12354
  }
12206
- const action = payload.action === "continue" || payload.action === "switch" || payload.action === "auto-roll" || payload.action === "setting" ? payload.action : null;
12355
+ const action = payload.action === "continue" || payload.action === "switch" || payload.action === "auto-roll" || payload.action === "auto-roll-accept" || payload.action === "auto-roll-cancel" || payload.action === "restart" || payload.action === "setting" ? payload.action : null;
12207
12356
  if (!action) {
12208
12357
  return;
12209
12358
  }
@@ -12215,6 +12364,7 @@ function startOfficialCodexBridgeActionListener(args) {
12215
12364
  const targetProfileName = typeof payload.targetProfileName === "string" && payload.targetProfileName.trim() ? payload.targetProfileName.trim().slice(0, 160) : null;
12216
12365
  const targetProfileKey = typeof payload.targetProfileKey === "string" && payload.targetProfileKey.trim() ? payload.targetProfileKey.trim().slice(0, 200) : null;
12217
12366
  const targetProfileKeyHash = typeof payload.targetProfileKeyHash === "string" && payload.targetProfileKeyHash.trim() ? payload.targetProfileKeyHash.trim().slice(0, 24) : null;
12367
+ const autoRollDecisionId = typeof payload.autoRollDecisionId === "string" && payload.autoRollDecisionId.trim() ? payload.autoRollDecisionId.trim().slice(0, 96) : null;
12218
12368
  const request = {
12219
12369
  id,
12220
12370
  action,
@@ -12222,6 +12372,7 @@ function startOfficialCodexBridgeActionListener(args) {
12222
12372
  targetProfileName,
12223
12373
  targetProfileKey,
12224
12374
  targetProfileKeyHash,
12375
+ autoRollDecisionId,
12225
12376
  settingKey,
12226
12377
  settingValue,
12227
12378
  cdpPort: args.cdpPort,
@@ -12671,17 +12822,6 @@ async function waitForNewMainPid(candidate, previousPids) {
12671
12822
  }
12672
12823
  return null;
12673
12824
  }
12674
- async function waitForMainPid(candidate, pid, timeoutMs) {
12675
- const deadline = Date.now() + timeoutMs;
12676
- while (Date.now() < deadline) {
12677
- const rows = await readProcessRows();
12678
- if (rows.some((row) => row.pid === pid && isMainProcessRow(row, candidate))) {
12679
- return true;
12680
- }
12681
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
12682
- }
12683
- return false;
12684
- }
12685
12825
  async function waitForAppServerPid(mainPid, profileHome) {
12686
12826
  const deadline = Date.now() + LAUNCH_WAIT_MS;
12687
12827
  while (Date.now() < deadline) {
@@ -12775,21 +12915,38 @@ async function resolveInstanceRuntimeState(args) {
12775
12915
  startedAt: mainRow.startedAt
12776
12916
  };
12777
12917
  }
12918
+ function classifyOpenCodexLaunchFailure(error) {
12919
+ const code = error && typeof error === "object" && "code" in error ? String(error.code ?? "") : "";
12920
+ const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
12921
+ const combined = `${code} ${message}`.toLowerCase();
12922
+ if (combined.includes("eperm") || combined.includes("eacces") || combined.includes("operation not permitted") || combined.includes("permission")) {
12923
+ return "disk-access-blocked";
12924
+ }
12925
+ return "open-spawn-failed";
12926
+ }
12778
12927
  async function openCodexWithProfileHome(candidate, profileHome, profileName, cdpPort, previousPids, options = {}) {
12779
12928
  const executablePath = getMainExecutablePath(candidate);
12780
12929
  if (!(0, import_node_fs6.existsSync)(executablePath)) {
12781
- return { opened: false, pid: null };
12930
+ return { opened: false, pid: null, reason: "app-executable-missing" };
12782
12931
  }
12783
12932
  const telemetryLabel = `codexuse-profile-${encodeURIComponent(profileName)}`;
12784
- const appUserDataDir = options.useDefaultUserData ? null : import_node_path9.default.join(profileHome, "codex-app-user-data");
12933
+ const appUserDataDir = options.useDefaultUserData === false ? import_node_path9.default.join(profileHome, "codex-app-user-data") : null;
12785
12934
  let child;
12786
12935
  try {
12787
12936
  if (appUserDataDir) {
12788
12937
  (0, import_node_fs6.mkdirSync)(appUserDataDir, { recursive: true });
12789
12938
  }
12790
12939
  child = (0, import_node_child_process5.spawn)(
12791
- executablePath,
12940
+ "/usr/bin/open",
12792
12941
  [
12942
+ "-n",
12943
+ "-a",
12944
+ candidate.appPath,
12945
+ "--env",
12946
+ `CODEX_HOME=${profileHome}`,
12947
+ "--env",
12948
+ `CODEX_TELEMETRY_LABEL=${telemetryLabel}`,
12949
+ "--args",
12793
12950
  ...appUserDataDir ? [`--user-data-dir=${appUserDataDir}`] : [],
12794
12951
  `--remote-debugging-port=${cdpPort}`,
12795
12952
  `--remote-debugging-address=${CODEX_CDP_HOST}`,
@@ -12797,39 +12954,37 @@ async function openCodexWithProfileHome(candidate, profileHome, profileName, cdp
12797
12954
  ],
12798
12955
  {
12799
12956
  detached: true,
12800
- env: {
12801
- ...process.env,
12802
- CODEX_HOME: profileHome,
12803
- CODEX_TELEMETRY_LABEL: telemetryLabel
12804
- },
12957
+ env: process.env,
12805
12958
  stdio: "ignore"
12806
12959
  }
12807
12960
  );
12808
- } catch {
12809
- return { opened: false, pid: null };
12810
- }
12811
- const childPid = child.pid ?? null;
12812
- child.unref();
12813
- const spawnError = await new Promise((resolve) => {
12814
- let settled = false;
12815
- const settle = (failed) => {
12816
- if (settled) {
12817
- return;
12818
- }
12819
- settled = true;
12820
- resolve(failed);
12961
+ } catch (error) {
12962
+ return {
12963
+ opened: false,
12964
+ pid: null,
12965
+ reason: classifyOpenCodexLaunchFailure(error)
12821
12966
  };
12822
- child.once("error", () => settle(true));
12823
- setTimeout(() => settle(false), POLL_INTERVAL_MS);
12824
- });
12825
- if (spawnError) {
12826
- return { opened: false, pid: null };
12827
12967
  }
12828
- if (childPid !== null) {
12829
- const verified = await waitForMainPid(candidate, childPid, 2e3);
12830
- if (verified) {
12831
- return { opened: true, pid: childPid };
12968
+ child.unref();
12969
+ const spawnError = await new Promise(
12970
+ (resolve) => {
12971
+ let settled = false;
12972
+ const settle = (reason) => {
12973
+ if (settled) {
12974
+ return;
12975
+ }
12976
+ settled = true;
12977
+ resolve(reason);
12978
+ };
12979
+ child.once(
12980
+ "error",
12981
+ (error) => settle(classifyOpenCodexLaunchFailure(error))
12982
+ );
12983
+ setTimeout(() => settle(null), POLL_INTERVAL_MS);
12832
12984
  }
12985
+ );
12986
+ if (spawnError) {
12987
+ return { opened: false, pid: null, reason: spawnError };
12833
12988
  }
12834
12989
  return {
12835
12990
  opened: true,
@@ -12874,6 +13029,35 @@ async function collectCodexAppCandidates(rawPaths) {
12874
13029
  }
12875
13030
  return candidates.sort((a, b) => a.appPath.localeCompare(b.appPath));
12876
13031
  }
13032
+ function addCodexAppPathsFromDirectory(rawPaths, rootPath, maxDepth = 2) {
13033
+ if (!(0, import_node_fs6.existsSync)(rootPath)) {
13034
+ return;
13035
+ }
13036
+ const pending = [{ directory: rootPath, depth: 0 }];
13037
+ while (pending.length > 0) {
13038
+ const current = pending.shift();
13039
+ let entries;
13040
+ try {
13041
+ entries = (0, import_node_fs6.readdirSync)(current.directory, { withFileTypes: true });
13042
+ } catch {
13043
+ continue;
13044
+ }
13045
+ for (const entry of entries) {
13046
+ if (!entry.isDirectory()) {
13047
+ continue;
13048
+ }
13049
+ const childPath = import_node_path9.default.join(current.directory, entry.name);
13050
+ if (entry.name === "Codex.app") {
13051
+ rawPaths.add(childPath);
13052
+ continue;
13053
+ }
13054
+ if (entry.name.endsWith(".app") || current.depth >= maxDepth) {
13055
+ continue;
13056
+ }
13057
+ pending.push({ directory: childPath, depth: current.depth + 1 });
13058
+ }
13059
+ }
13060
+ }
12877
13061
  async function discoverCodexAppCandidates() {
12878
13062
  const now = Date.now();
12879
13063
  const envPath = process.env.CODEXUSE_OFFICIAL_CODEX_APP_PATH?.trim() || null;
@@ -12886,6 +13070,8 @@ async function discoverCodexAppCandidates() {
12886
13070
  }
12887
13071
  rawPaths.add("/Applications/Codex.app");
12888
13072
  rawPaths.add(import_node_path9.default.join((0, import_node_os3.homedir)(), "Applications", "Codex.app"));
13073
+ addCodexAppPathsFromDirectory(rawPaths, "/Applications");
13074
+ addCodexAppPathsFromDirectory(rawPaths, import_node_path9.default.join((0, import_node_os3.homedir)(), "Applications"));
12889
13075
  const sorted = await collectCodexAppCandidates(rawPaths);
12890
13076
  appDiscoveryCache = {
12891
13077
  envPath,
@@ -13015,17 +13201,30 @@ async function getOfficialCodexProfileInstances(options = {}) {
13015
13201
  const managedPids = /* @__PURE__ */ new Set();
13016
13202
  for (const instance of Object.values(state.officialCodex.instancesByProfileName)) {
13017
13203
  const runtime = await resolveInstanceRuntimeState({ instance, candidate, rows });
13204
+ let current = instance;
13205
+ if (!runtime.running && (instance.pid !== null || instance.appServerPid !== null || instance.cdpPort !== null)) {
13206
+ current = {
13207
+ ...instance,
13208
+ pid: null,
13209
+ appServerPid: null,
13210
+ ...bridgeStateUnavailable(),
13211
+ lastVerifiedAt: Date.now(),
13212
+ lastStatus: "not-running",
13213
+ lastError: null
13214
+ };
13215
+ await patchManagedInstance(current);
13216
+ }
13018
13217
  if (runtime.running && instance.pid) {
13019
13218
  managedPids.add(instance.pid);
13020
13219
  }
13021
13220
  if (allowedProfileNames && !allowedProfileNames.has(instance.profileName)) {
13022
13221
  continue;
13023
13222
  }
13024
- let current = instance;
13025
13223
  if (runtime.running && options.forceBridgeRefresh === true) {
13224
+ const previousBridgeLastCheckedAt = current.bridgeLastCheckedAt;
13026
13225
  const bridge = await refreshBridgeStateIfNeeded(
13027
13226
  {
13028
- ...instance,
13227
+ ...current,
13029
13228
  appServerPid: runtime.appServerPid
13030
13229
  },
13031
13230
  true,
@@ -13033,14 +13232,14 @@ async function getOfficialCodexProfileInstances(options = {}) {
13033
13232
  true
13034
13233
  );
13035
13234
  current = {
13036
- ...instance,
13235
+ ...current,
13037
13236
  appServerPid: runtime.appServerPid,
13038
13237
  ...bridge,
13039
- lastVerifiedAt: bridge.bridgeLastCheckedAt ?? instance.lastVerifiedAt,
13238
+ lastVerifiedAt: bridge.bridgeLastCheckedAt ?? current.lastVerifiedAt,
13040
13239
  lastStatus: bridge.bridgeStatus === "healthy" ? "bridged" : "running-unbridged",
13041
13240
  lastError: null
13042
13241
  };
13043
- if (bridge.bridgeLastCheckedAt !== instance.bridgeLastCheckedAt) {
13242
+ if (bridge.bridgeLastCheckedAt !== previousBridgeLastCheckedAt) {
13044
13243
  await patchManagedInstance(current);
13045
13244
  }
13046
13245
  }
@@ -13100,6 +13299,9 @@ async function getOfficialCodexProfileInstances(options = {}) {
13100
13299
  lastError: null
13101
13300
  };
13102
13301
  }
13302
+ if (!existing || existing.pid !== current.pid || existing.appServerPid !== current.appServerPid || existing.cdpPort !== current.cdpPort || options.forceBridgeRefresh === true && existing.bridgeLastCheckedAt !== current.bridgeLastCheckedAt || existing.lastStatus !== current.lastStatus) {
13303
+ await patchManagedInstance(current);
13304
+ }
13103
13305
  await ensureBridgeActionListenerForInstance(current);
13104
13306
  managed.push(
13105
13307
  managedInstanceToPayload(current, true, appServerPid, startedAt)
@@ -13231,6 +13433,7 @@ async function launchOfficialCodexProfileInstanceOnce(options) {
13231
13433
  new Set(mainPids)
13232
13434
  );
13233
13435
  if (!launch.opened) {
13436
+ const reason = launch.reason ?? "open-failed";
13234
13437
  const failed = {
13235
13438
  profileName: options.profileName,
13236
13439
  profileKey: options.profileKey,
@@ -13245,14 +13448,14 @@ async function launchOfficialCodexProfileInstanceOnce(options) {
13245
13448
  launchedAt: null,
13246
13449
  lastVerifiedAt: null,
13247
13450
  lastStatus: "failed",
13248
- lastError: "open-failed"
13451
+ lastError: reason
13249
13452
  };
13250
13453
  await patchManagedInstance(failed);
13251
13454
  return {
13252
13455
  status: "failed",
13253
13456
  profileName: options.profileName,
13254
13457
  instance: managedInstanceToPayload(failed, false),
13255
- reason: "open-failed"
13458
+ reason
13256
13459
  };
13257
13460
  }
13258
13461
  const launchedPid = launch.pid;
@@ -15954,7 +16157,7 @@ async function ensureCliStorageReady() {
15954
16157
  }
15955
16158
 
15956
16159
  // src/app/main.ts
15957
- var VERSION = true ? "5.0.1" : "0.0.0";
16160
+ var VERSION = true ? "5.0.2" : "0.0.0";
15958
16161
  async function runCli() {
15959
16162
  const args = process.argv.slice(2);
15960
16163
  if (args.length === 0) {