Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
463696a
add persistent account footer and toast cleanup
ndycode Mar 14, 2026
ee7b855
fix footer review findings
ndycode Mar 14, 2026
15867c9
fix stale footer account counts
ndycode Mar 14, 2026
4735177
Avoid extra footer storage reads during loader init
ndycode Mar 14, 2026
0577c35
Cache footer config for switch refreshes
ndycode Mar 14, 2026
ede4db3
Keep footer config synced across runtime refreshes
ndycode Mar 14, 2026
ca66060
Align footer refresh regression with runtime config flow
ndycode Mar 14, 2026
6f89869
Stabilize persisted account footer metadata
ndycode Mar 14, 2026
7c0671f
Fix persisted footer switch regression test
ndycode Mar 14, 2026
fabb18e
Harden persisted footer fetch guards
ndycode Mar 14, 2026
d6b469e
Fix terminal toast await and event guard test
ndycode Mar 14, 2026
f09985a
Harden footer indicator transform coverage
ndycode Mar 14, 2026
4e78502
Clarify footer LRU semantics
ndycode Mar 14, 2026
19262a5
Keep UI runtime refresh side-effect free
ndycode Mar 14, 2026
720bc56
Cache footer config for auth flows
ndycode Mar 14, 2026
6aba74a
Trim footer test helper and revision churn
ndycode Mar 14, 2026
8ef68b9
Fix first-switch footer feedback gap
ndycode Mar 14, 2026
912932a
Align footer session lookup with thread id
ndycode Mar 14, 2026
3359386
Guard chat footer decoration by role
ndycode Mar 14, 2026
bdb4135
Tighten live chat footer coverage
ndycode Mar 14, 2026
7bf5fbc
Tighten chat footer role guards
ndycode Mar 14, 2026
c7e10e3
Harden footer count hint regression test
ndycode Mar 14, 2026
50249be
Clear stale footer indicators when disabled
ndycode Mar 14, 2026
3bc1d89
Optimize persisted footer refresh
ndycode Mar 15, 2026
16e6851
Clarify footer refresh naming
ndycode Mar 15, 2026
59c2ec4
Merge branch 'main' into feature/footer-toast-cleanup
ndycode Mar 15, 2026
717938e
Reset footer count hint on disable
ndycode Mar 15, 2026
cec8b8d
Align persisted footer test output types
ndycode Mar 15, 2026
002c0d7
Refresh auth storage path config on authorize
ndycode Mar 15, 2026
380c5af
Harden footer review regressions
ndycode Mar 15, 2026
84e60f0
Harden authorize config fallback
ndycode Mar 15, 2026
dbf09a6
Avoid duplicate cold-start authorize reads
ndycode Mar 15, 2026
781add5
Finish footer review cleanup
ndycode Mar 15, 2026
ac9a951
Align footer hook session key priority
ndycode Mar 15, 2026
5adf448
Tighten persisted footer runtime helpers
ndycode Mar 15, 2026
99c76e8
test: cover persisted footer edge cases
ndycode Mar 15, 2026
cc0da76
fix: harden authorize config fallback detection
ndycode Mar 15, 2026
ba9e7a5
fix: tighten persisted footer contracts
ndycode Mar 15, 2026
65722bc
fix: reuse cached ui runtime config
ndycode Mar 15, 2026
9dd973a
fix: preserve footer runtime config on authorize refresh
ndycode Mar 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
533 changes: 465 additions & 68 deletions index.ts

Large diffs are not rendered by default.

64 changes: 59 additions & 5 deletions lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { readFileSync, existsSync } from "node:fs";
import { join } from "node:path";
import { homedir } from "node:os";
import type { PluginConfig } from "./types.js";
import {
PERSIST_ACCOUNT_FOOTER_STYLES,
type PersistAccountFooterStyle,
} from "./persist-account-footer.js";
import {
normalizeRetryBudgetValue,
type RetryBudgetOverrides,
Expand All @@ -16,14 +20,28 @@ const TUI_GLYPH_MODES = new Set(["ascii", "unicode", "auto"]);
const REQUEST_TRANSFORM_MODES = new Set(["native", "legacy"]);
const UNSUPPORTED_CODEX_POLICIES = new Set(["strict", "fallback"]);
const RETRY_PROFILES = new Set(["conservative", "balanced", "aggressive"]);

const FALLBACK_PLUGIN_CONFIG = Symbol("fallbackPluginConfig");
export type UnsupportedCodexPolicy = "strict" | "fallback";
type PluginConfigWithFallbackMarker = PluginConfig & {
[FALLBACK_PLUGIN_CONFIG]?: true;
};

const markFallbackPluginConfig = <T extends PluginConfig>(config: T): T => {
if ((config as PluginConfigWithFallbackMarker)[FALLBACK_PLUGIN_CONFIG]) {
return config;
}
Object.defineProperty(config, FALLBACK_PLUGIN_CONFIG, {
value: true,
enumerable: false,
});
return config;
};

/**
* Default plugin configuration
* CODEX_MODE is enabled by default for better Codex CLI parity
*/
const DEFAULT_CONFIG: PluginConfig = {
export const DEFAULT_CONFIG: PluginConfig = markFallbackPluginConfig({
codexMode: true,
requestTransformMode: "native",
codexTuiV2: true,
Expand All @@ -45,6 +63,8 @@ const DEFAULT_CONFIG: PluginConfig = {
tokenRefreshSkewMs: 60_000,
rateLimitToastDebounceMs: 60_000,
toastDurationMs: 5_000,
persistAccountFooter: false,
persistAccountFooterStyle: "label-masked-email",
perProjectAccounts: true,
sessionRecovery: true,
autoResume: true,
Expand All @@ -55,7 +75,22 @@ const DEFAULT_CONFIG: PluginConfig = {
pidOffsetEnabled: false,
fetchTimeoutMs: 60_000,
streamStallTimeoutMs: 45_000,
};
});

function createFallbackPluginConfig(): PluginConfig {
// Spread drops the non-enumerable fallback marker, so exact-default config
// files stay distinct from loader fallbacks.
return markFallbackPluginConfig({ ...DEFAULT_CONFIG });
}

export function isFallbackPluginConfig(
pluginConfig: PluginConfig | undefined,
): boolean {
return !!(
pluginConfig &&
(pluginConfig as PluginConfigWithFallbackMarker)[FALLBACK_PLUGIN_CONFIG]
);
}

/**
* Load plugin configuration from ~/.opencode/openai-codex-auth-config.json
Expand All @@ -66,7 +101,7 @@ const DEFAULT_CONFIG: PluginConfig = {
export function loadPluginConfig(): PluginConfig {
try {
if (!existsSync(CONFIG_PATH)) {
return DEFAULT_CONFIG;
return createFallbackPluginConfig();
}

const fileContent = readFileSync(CONFIG_PATH, "utf-8");
Expand Down Expand Up @@ -102,7 +137,7 @@ export function loadPluginConfig(): PluginConfig {
logWarn(
`Failed to load config from ${CONFIG_PATH}: ${(error as Error).message}`,
);
return DEFAULT_CONFIG;
return createFallbackPluginConfig();
}
}

Expand Down Expand Up @@ -433,6 +468,25 @@ export function getToastDurationMs(pluginConfig: PluginConfig): number {
);
}

export function getPersistAccountFooter(pluginConfig: PluginConfig): boolean {
return resolveBooleanSetting(
"CODEX_AUTH_PERSIST_ACCOUNT_FOOTER",
pluginConfig.persistAccountFooter,
false,
);
}

export function getPersistAccountFooterStyle(
pluginConfig: PluginConfig,
): PersistAccountFooterStyle {
return resolveStringSetting(
"CODEX_AUTH_PERSIST_ACCOUNT_FOOTER_STYLE",
pluginConfig.persistAccountFooterStyle,
"label-masked-email",
new Set(PERSIST_ACCOUNT_FOOTER_STYLES),
);
}

export function getPerProjectAccounts(pluginConfig: PluginConfig): boolean {
return resolveBooleanSetting(
"CODEX_AUTH_PER_PROJECT_ACCOUNTS",
Expand Down
29 changes: 29 additions & 0 deletions lib/persist-account-footer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { AccountIdSource } from "./types.js";

export const PERSIST_ACCOUNT_FOOTER_STYLES = [
"label-masked-email",
"full-email",
"label-only",
] as const;

export type PersistAccountFooterStyle =
(typeof PERSIST_ACCOUNT_FOOTER_STYLES)[number];

export type PersistedAccountDetails = {
accountId?: string;
accountIdSource?: AccountIdSource;
accountLabel?: string;
email?: string;
access?: string;
accessToken?: string;
};

export type SessionModelRef = {
providerID: string;
modelID: string;
};

export type PersistedAccountIndicatorEntry = {
label: string;
revision: number;
};
3 changes: 3 additions & 0 deletions lib/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Types are inferred from schemas using z.infer.
*/
import { z } from "zod";
import { PERSIST_ACCOUNT_FOOTER_STYLES } from "./persist-account-footer.js";
import { MODEL_FAMILIES, type ModelFamily } from "./prompts/codex.js";

// ============================================================================
Expand Down Expand Up @@ -42,6 +43,8 @@ export const PluginConfigSchema = z.object({
tokenRefreshSkewMs: z.number().min(0).optional(),
rateLimitToastDebounceMs: z.number().min(0).optional(),
toastDurationMs: z.number().min(1000).optional(),
persistAccountFooter: z.boolean().optional(),
persistAccountFooterStyle: z.enum(PERSIST_ACCOUNT_FOOTER_STYLES).optional(),
perProjectAccounts: z.boolean().optional(),
sessionRecovery: z.boolean().optional(),
autoResume: z.boolean().optional(),
Expand Down
Loading