From 37d53791fa820e2b049b13a15faa25c7994eb05b Mon Sep 17 00:00:00 2001 From: priva7eer Date: Sat, 17 Oct 2026 10:28:51 +0200 Subject: [PATCH 1/2] Add opt-in local repair for repeated Codex onboarding --- CHANGELOG.md | 6 +++ README.md | 8 ++++ docs/local-onboarding-fix.md | 57 +++++++++++++++++++++++++++ scripts/local-onboarding-fix.cjs | 63 ++++++++++++++++++++++++++++++ test/local-onboarding-fix.test.cjs | 60 ++++++++++++++++++++++++++++ 5 files changed, 194 insertions(+) create mode 100644 docs/local-onboarding-fix.md create mode 100644 scripts/local-onboarding-fix.cjs create mode 100644 test/local-onboarding-fix.test.cjs diff --git a/CHANGELOG.md b/CHANGELOG.md index 848f26d..e263b0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to Codex Extras are documented in this file. +## Unreleased + +- Add an optional, version-checked developer repair for repeated onboarding in + OpenAI Codex 26.1007.21434, with backup, rollback and regression tests. The + repair is excluded from the Extras VSIX and never runs automatically. + ## 1.1.0 - Add a Codex Chats Activity Bar panel with local chat history, automatic diff --git a/README.md b/README.md index b5c5777..ea0d506 100644 --- a/README.md +++ b/README.md @@ -81,3 +81,11 @@ Codex Extras is an independent, unofficial open-source project. It is not affili ## License [MIT](LICENSE) + +## Local onboarding troubleshooting + +If the official Codex welcome screen repeatedly appears after completion, see +[the optional local onboarding repair](docs/local-onboarding-fix.md) for OpenAI +extension version `26.1007.21434`. This developer script changes the installed +OpenAI extension only when explicitly run, keeps backups, and supports rollback. +It is excluded from the Extras VSIX and does not run automatically. diff --git a/docs/local-onboarding-fix.md b/docs/local-onboarding-fix.md new file mode 100644 index 0000000..23c1dda --- /dev/null +++ b/docs/local-onboarding-fix.md @@ -0,0 +1,57 @@ +# Local OpenAI Codex onboarding repair + +This is a development repair of `openai.chatgpt` version `26.1007.21434`. +It is not part of the Codex Extras runtime or VSIX. Do not publish this as an +Extras feature: the defective onboarding decision belongs to OpenAI's webview. + +The installed extension's `FGi()` checks `isLoading` before reading the saved +completion flag. Failed and paused requests can have `isLoading: false` without +successful data, so they incorrectly select onboarding. The repair requires +`isSuccess` instead. It also makes an already open onboarding page navigate home +when the saved completion flag becomes available and says onboarding is complete. +A failed read remains on the loading screen until a successful refetch; this +repair does not diagnose or repair the underlying request failure. + +Local inspection confirmed both saved completion flags were true. Isolated tests +reproduce the incorrect decision in the original installed code. The particular +request failure triggering the user's repeated welcome screen is not yet proven. +After applying the repair and reloading VS Code, the reporter confirmed that the repeated welcome screen stopped appearing. The exact triggering request failure remains unverified. + +## Apply + +From the repository root, pass the absolute installed extension directory: + +```powershell +node scripts/local-onboarding-fix.cjs "$env:USERPROFILE/.vscode/extensions/openai.chatgpt-26.1007.21434-win32-x64" +``` + +Both target files are validated before writes. Original files are saved alongside +them with `.codex-extras-nux-backup` appended. The script rejects unsupported +versions and unexpected code. Reapplying the repair is safe. + +Run **Developer: Reload Window** from the VS Code command palette. Then open a +new agent through both the Extras toolbar and the Codex Chats plus button, +including while another empty agent is open. Repeat after another window reload. +The welcome screen should not return and existing conversations should still open. + +An OpenAI extension update may replace these files. Do not automatically reapply +the repair to another version; inspect and test its code first. + +## Restore + +```powershell +node scripts/local-onboarding-fix.cjs "$env:USERPROFILE/.vscode/extensions/openai.chatgpt-26.1007.21434-win32-x64" --restore +``` + +Reload the VS Code window again. Restore refuses to overwrite files that changed +after the repair. + +## Tests + +```powershell +node --test test/local-onboarding-fix.test.cjs +npm.cmd test +``` + +The focused tests cover the original regression, legitimate first use, completion, +an already open welcome page, version validation, backup, idempotence and restore. diff --git a/scripts/local-onboarding-fix.cjs b/scripts/local-onboarding-fix.cjs new file mode 100644 index 0000000..da00063 --- /dev/null +++ b/scripts/local-onboarding-fix.cjs @@ -0,0 +1,63 @@ +"use strict"; + +// Local repair for OpenAI's extension, NOT part of the Codex Extras runtime. +const fs = require("node:fs"); +const path = require("node:path"); +const crypto = require("node:crypto"); + +const version = "26.1007.21434"; +const guardBefore = "function FGi(){let{data:e,isLoading:t}=UC(hs.NUX_2025_09_15),{authMethod:n}=Lf();if(!t)return e||n==null?`none`:n===`chatgptAuthTokens`||n===`chatgpt`?`2025-09-15-full-chatgpt-auth`:`2025-09-15-apikey-auth`}"; +const guardAfter = guardBefore.replace("isLoading:t", "isSuccess:t").replace("if(!t)", "if(t)"); +const pageBefore = "let s=h(),l;e[0]!==s||e[1]!==n||e[2]!==t?"; +const pageAfter = "let s=h(),l;X.useEffect(()=>{if(n===`none`)s(`/`)},[n,s]);e[0]!==s||e[1]!==n||e[2]!==t?"; +const patches = [ + { file: "app-initial-c014f9ee4429.js", before: guardBefore, after: guardAfter }, + { file: "first-run-afdf3f60ec3c.js", before: pageBefore, after: pageAfter }, +]; + +function transform(source, before, after) { + if (source.includes(after) && !source.includes(before)) return source; + if (source.split(before).length !== 2 || source.includes(after)) { + throw new Error("Unrecognized extension code; refusing to patch"); + } + return source.replace(before, after); +} + +function apply(extensionDirectory, restore = false) { + const manifest = JSON.parse(fs.readFileSync(path.join(extensionDirectory, "package.json"), "utf8")); + if (manifest.publisher !== "openai" || manifest.name !== "chatgpt" || manifest.version !== version) { + throw new Error(`Repair supports only openai.chatgpt ${version}`); + } + // Validate BOTH files before writing either one. Never overwrite a backup. + const changes = patches.map(({ file, before, after }) => { + const target = path.join(extensionDirectory, "webview", "assets", file); + const backup = `${target}.codex-extras-nux-backup`; + const original = fs.readFileSync(target, "utf8"); + const repaired = transform(original, before, after); + if (restore) { + const saved = fs.readFileSync(backup, "utf8"); + if (transform(saved, before, after) !== original) throw new Error("File changed since repair; refusing restore"); + return { target, content: saved }; + } + if (fs.existsSync(backup)) { + if (transform(fs.readFileSync(backup, "utf8"), before, after) !== repaired) { + throw new Error("Backup does not match installed extension"); + } + } else if (original !== repaired) { + return { target, backup, original, content: repaired }; + } + return { target, content: repaired }; + }); + for (const change of changes) { + if (change.backup) fs.writeFileSync(change.backup, change.original, { flag: "wx" }); + fs.writeFileSync(change.target, change.content); + console.log(`${restore ? "Restored" : "Patched"} ${path.basename(change.target)} sha256=${crypto.createHash("sha256").update(change.content).digest("hex")}`); + } +} + +if (require.main === module) { + if (!process.argv[2]) throw new Error("Usage: node scripts/local-onboarding-fix.cjs EXTENSION_DIRECTORY [--restore]"); + apply(path.resolve(process.argv[2]), process.argv.includes("--restore")); +} + +module.exports = { apply, transform, patches, guardBefore, guardAfter, pageAfter, version }; diff --git a/test/local-onboarding-fix.test.cjs b/test/local-onboarding-fix.test.cjs new file mode 100644 index 0000000..cec3f8b --- /dev/null +++ b/test/local-onboarding-fix.test.cjs @@ -0,0 +1,60 @@ +"use strict"; +const assert = require("node:assert/strict"); +const test = require("node:test"); +const vm = require("node:vm"); +const fs = require("node:fs"); +const os = require("node:os"); +const path = require("node:path"); +const { apply, transform, patches, guardBefore, guardAfter, pageAfter, version } = require("../scripts/local-onboarding-fix.cjs"); + +function decide(source, query, authMethod = "chatgpt") { + return vm.runInNewContext(`${source}; FGi()`, { + UC: () => query, hs: { NUX_2025_09_15: "completed" }, Lf: () => ({ authMethod }), + }); +} + +test("regression: a paused or failed read must never reopen onboarding", () => { + for (const query of [ + { data: undefined, isLoading: false, isSuccess: false, isPending: true, fetchStatus: "paused" }, + { data: undefined, isLoading: false, isSuccess: false, isError: true }, + ]) { + assert.equal(decide(guardBefore, query), "2025-09-15-full-chatgpt-auth"); + assert.equal(decide(guardAfter, query), undefined); + } +}); + +test("successful reads preserve genuine first use and completed onboarding", () => { + assert.equal(decide(guardAfter, { data: true, isSuccess: true }), "none"); + assert.equal(decide(guardAfter, { data: false, isSuccess: true }), "2025-09-15-full-chatgpt-auth"); + assert.equal(decide(guardAfter, { data: false, isSuccess: true }, "apikey"), "2025-09-15-apikey-auth"); + assert.equal(decide(guardAfter, { data: false, isSuccess: true }, null), "none"); + assert.equal(decide(guardAfter, { data: undefined, isSuccess: false, isLoading: true }), undefined); +}); + +test("an already open welcome page exits after a successful completion read", () => { + const effect = pageAfter.slice(pageAfter.indexOf("X.useEffect"), pageAfter.indexOf(";e[0]")); + for (const state of [undefined, "2025-09-15-full-chatgpt-auth", "none"]) { + const routes = []; + vm.runInNewContext(effect, { X: { useEffect: (fn) => fn() }, n: state, s: (route) => routes.push(route) }); + assert.deepEqual(routes, state === "none" ? ["/"] : []); + } +}); + +test("local deployment is version checked, idempotent, backed up and reversible", (t) => { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), "codex-nux-fix-")); + t.after(() => fs.rmSync(dir, { recursive: true, force: true })); + fs.mkdirSync(path.join(dir, "webview", "assets"), { recursive: true }); + fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify({ publisher: "openai", name: "chatgpt", version })); + for (const patch of patches) fs.writeFileSync(path.join(dir, "webview", "assets", patch.file), patch.before); + apply(dir); + apply(dir); + for (const patch of patches) assert.equal(fs.readFileSync(path.join(dir, "webview", "assets", patch.file), "utf8"), patch.after); + apply(dir, true); + for (const patch of patches) assert.equal(fs.readFileSync(path.join(dir, "webview", "assets", patch.file), "utf8"), patch.before); + fs.writeFileSync(path.join(dir, "webview", "assets", patches[1].file), "unexpected build"); + assert.throws(() => apply(dir), /Unrecognized/); + assert.equal(fs.readFileSync(path.join(dir, "webview", "assets", patches[0].file), "utf8"), patches[0].before); + assert.throws(() => transform("unknown", patches[0].before, patches[0].after), /Unrecognized/); + fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify({ publisher: "openai", name: "chatgpt", version: "other" })); + assert.throws(() => apply(dir), /supports only/); +}); From 89c1daa2ba39a3f00c8a9d3db952bab483519090 Mon Sep 17 00:00:00 2001 From: priva7eer Date: Sat, 17 Oct 2026 12:11:54 +0200 Subject: [PATCH 2/2] Document task interruption risk when reloading Codex --- docs/local-onboarding-fix.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/local-onboarding-fix.md b/docs/local-onboarding-fix.md index 23c1dda..08025b3 100644 --- a/docs/local-onboarding-fix.md +++ b/docs/local-onboarding-fix.md @@ -29,9 +29,12 @@ Both target files are validated before writes. Original files are saved alongsid them with `.codex-extras-nux-backup` appended. The script rejects unsupported versions and unexpected code. Reapplying the repair is safe. -Run **Developer: Reload Window** from the VS Code command palette. Then open a +Wait until all local Codex tasks in the window have finished before running +**Developer: Reload Window** from the VS Code command palette. Reload restarts +the official Codex extension and can interrupt its running tasks. Then open a new agent through both the Extras toolbar and the Codex Chats plus button, -including while another empty agent is open. Repeat after another window reload. +including while another empty agent is open. Repeat after another window reload, +again waiting for active tasks to finish first. The welcome screen should not return and existing conversations should still open. An OpenAI extension update may replace these files. Do not automatically reapply @@ -43,7 +46,8 @@ the repair to another version; inspect and test its code first. node scripts/local-onboarding-fix.cjs "$env:USERPROFILE/.vscode/extensions/openai.chatgpt-26.1007.21434-win32-x64" --restore ``` -Reload the VS Code window again. Restore refuses to overwrite files that changed +Reload the VS Code window again after active tasks have finished. +Restore refuses to overwrite files that changed after the repair. ## Tests