From 86d56076ad4bd46431cef5d50d72924e8295bbab Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Thu, 2 Apr 2026 13:30:41 -0600 Subject: [PATCH 01/21] Add CodexSideloader shell contribution to replace extension-sideloader extension Moves sideloading logic into the Codex shell as a Workbench Contribution that uses direct IExtensionGalleryService/IWorkbenchExtensionManagementService access instead of the public installExtension command, runs deterministically at WorkbenchPhase.AfterRestored, and reads configuration from product.json. ## Changes - New source overlay: codexSideloader contribution (2 files) - New patch: feat-codex-sideloader.patch (registers in workbench.common.main.ts) - product.json: add codexSideloadExtensions config array - bundle-extensions.json: remove extension-sideloader entry --- bundle-extensions.json | 8 +- patches/feat-codex-sideloader.patch | 8 ++ product.json | 8 +- .../browser/codexSideloader.contribution.ts | 9 ++ .../browser/codexSideloader.ts | 93 +++++++++++++++++++ 5 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 patches/feat-codex-sideloader.patch create mode 100644 src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.contribution.ts create mode 100644 src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts diff --git a/bundle-extensions.json b/bundle-extensions.json index adc32f6fa39..95d1e64aa58 100644 --- a/bundle-extensions.json +++ b/bundle-extensions.json @@ -1,9 +1,3 @@ { - "bundle": [ - { - "name": "extension-sideloader", - "github_release": "genesis-ai-dev/extension-sideloader", - "tag": "0.1.0" - } - ] + "bundle": [] } diff --git a/patches/feat-codex-sideloader.patch b/patches/feat-codex-sideloader.patch new file mode 100644 index 00000000000..2dc8fdb6b2a --- /dev/null +++ b/patches/feat-codex-sideloader.patch @@ -0,0 +1,8 @@ +diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts +index 5ede7d5..89fcb25 100644 +--- a/src/vs/workbench/workbench.common.main.ts ++++ b/src/vs/workbench/workbench.common.main.ts +@@ -327,2 +327,3 @@ import './contrib/keybindings/browser/keybindings.contribution.js'; + import './contrib/codexConductor/browser/codexConductor.contribution.js'; ++import './contrib/codexSideloader/browser/codexSideloader.contribution.js'; + diff --git a/product.json b/product.json index 2f75e161d31..fa195c4d3d6 100644 --- a/product.json +++ b/product.json @@ -598,5 +598,11 @@ "gruntfuggly.todo-tree": { "default": false } - } + }, + "codexSideloadExtensions": [ + "project-accelerate.codex-editor-extension", + "project-accelerate.shared-state-store", + "project-accelerate.vscode-edit-table", + "frontier-rnd.frontier-authentication" + ] } diff --git a/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.contribution.ts b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.contribution.ts new file mode 100644 index 00000000000..ec5a3e76271 --- /dev/null +++ b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.contribution.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Frontier R&D Ltd. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; +import { CodexSideloaderContribution } from './codexSideloader.js'; + +registerWorkbenchContribution2(CodexSideloaderContribution.ID, CodexSideloaderContribution, WorkbenchPhase.AfterRestored); diff --git a/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts new file mode 100644 index 00000000000..737150a3d5d --- /dev/null +++ b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Frontier R&D Ltd. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { IWorkbenchExtensionManagementService } from '../../../services/extensionManagement/common/extensionManagement.js'; +import { IExtensionGalleryService } from '../../../../platform/extensionManagement/common/extensionManagement.js'; +import { IProductService } from '../../../../platform/product/common/productService.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; +import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { ExtensionType } from '../../../../platform/extensions/common/extensions.js'; + +const TAG = '[CodexSideloader]'; + +export class CodexSideloaderContribution extends Disposable implements IWorkbenchContribution { + + static readonly ID = 'workbench.contrib.codexSideloader'; + + constructor( + @IWorkbenchExtensionManagementService private readonly extensionManagementService: IWorkbenchExtensionManagementService, + @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, + @IProductService private readonly productService: IProductService, + @ILogService private readonly logService: ILogService, + @INotificationService private readonly notificationService: INotificationService, + ) { + super(); + + const configured = (this.productService as Record)['codexSideloadExtensions']; + if (!Array.isArray(configured) || configured.length === 0) { + this.logService.info(`${TAG} No sideload extensions configured in product.json`); + return; + } + + const extensionIds: string[] = configured.filter((id): id is string => typeof id === 'string'); + if (extensionIds.length === 0) { + return; + } + + this.ensureExtensions(extensionIds).catch(err => { + this.logService.error(`${TAG} Unhandled error during sideload`, err); + }); + } + + private async ensureExtensions(extensionIds: string[]): Promise { + // Determine which extensions are already installed + const installed = await this.extensionManagementService.getInstalled(ExtensionType.User); + const installedIds = new Set(installed.map(e => e.identifier.id.toLowerCase())); + + const missing = extensionIds.filter(id => !installedIds.has(id.toLowerCase())); + if (missing.length === 0) { + this.logService.info(`${TAG} All sideload extensions already installed`); + return; + } + + this.logService.info(`${TAG} Installing ${missing.length} missing extension(s): ${missing.join(', ')}`); + + // Check if gallery service is available + if (!this.extensionGalleryService.isEnabled()) { + this.logService.warn(`${TAG} Extension gallery is not available — skipping sideload`); + return; + } + + // Resolve extension IDs to gallery entries + const galleryExtensions = await this.extensionGalleryService.getExtensions( + missing.map(id => ({ id })), + CancellationToken.None + ); + + const resolved = new Map(galleryExtensions.map(ext => [ext.identifier.id.toLowerCase(), ext])); + + for (const id of missing) { + const galleryExt = resolved.get(id.toLowerCase()); + if (!galleryExt) { + this.logService.warn(`${TAG} Extension "${id}" not found in gallery — skipping`); + continue; + } + + try { + await this.extensionManagementService.installFromGallery(galleryExt); + this.logService.info(`${TAG} Installed "${id}" v${galleryExt.version}`); + } catch (err) { + this.logService.error(`${TAG} Failed to install "${id}"`, err); + this.notificationService.notify({ + severity: Severity.Warning, + message: `Codex: Failed to install extension "${id}". It may be installed manually from the Extensions view.`, + }); + } + } + } +} From f256b5797fe00148257c4d63467686f18073abcb Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Thu, 2 Apr 2026 13:37:30 -0600 Subject: [PATCH 02/21] Add VSIX URL support to CodexSideloader Config entries can now be either a string (gallery install) or an object with `id` and `vsix` fields (direct VSIX install via shared process IPC, bypassing the marketplace). Gallery and VSIX installs run in parallel. --- .../browser/codexSideloader.ts | 103 +++++++++++++++--- 1 file changed, 90 insertions(+), 13 deletions(-) diff --git a/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts index 737150a3d5d..61dafaca9d4 100644 --- a/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts +++ b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts @@ -10,11 +10,37 @@ import { IExtensionGalleryService } from '../../../../platform/extensionManageme import { IProductService } from '../../../../platform/product/common/productService.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; +import { ISharedProcessService } from '../../../../platform/ipc/electron-browser/services.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { ExtensionType } from '../../../../platform/extensions/common/extensions.js'; +import { URI } from '../../../../base/common/uri.js'; const TAG = '[CodexSideloader]'; +/** A string means "install from gallery by ID". An object with `vsix` means "install directly from URL". */ +interface SideloadVsixEntry { + id: string; + vsix: string; +} + +type SideloadEntry = string | SideloadVsixEntry; + +function parseSideloadEntries(raw: unknown[]): SideloadEntry[] { + const entries: SideloadEntry[] = []; + for (const item of raw) { + if (typeof item === 'string') { + entries.push(item); + } else if ( + item && typeof item === 'object' && + typeof (item as Record).id === 'string' && + typeof (item as Record).vsix === 'string' + ) { + entries.push(item as SideloadVsixEntry); + } + } + return entries; +} + export class CodexSideloaderContribution extends Disposable implements IWorkbenchContribution { static readonly ID = 'workbench.contrib.codexSideloader'; @@ -25,6 +51,7 @@ export class CodexSideloaderContribution extends Disposable implements IWorkbenc @IProductService private readonly productService: IProductService, @ILogService private readonly logService: ILogService, @INotificationService private readonly notificationService: INotificationService, + @ISharedProcessService private readonly sharedProcessService: ISharedProcessService, ) { super(); @@ -34,44 +61,66 @@ export class CodexSideloaderContribution extends Disposable implements IWorkbenc return; } - const extensionIds: string[] = configured.filter((id): id is string => typeof id === 'string'); - if (extensionIds.length === 0) { + const entries = parseSideloadEntries(configured); + if (entries.length === 0) { return; } - this.ensureExtensions(extensionIds).catch(err => { + this.ensureExtensions(entries).catch(err => { this.logService.error(`${TAG} Unhandled error during sideload`, err); }); } - private async ensureExtensions(extensionIds: string[]): Promise { - // Determine which extensions are already installed + private async ensureExtensions(entries: SideloadEntry[]): Promise { const installed = await this.extensionManagementService.getInstalled(ExtensionType.User); const installedIds = new Set(installed.map(e => e.identifier.id.toLowerCase())); - const missing = extensionIds.filter(id => !installedIds.has(id.toLowerCase())); - if (missing.length === 0) { + const missingGallery: string[] = []; + const missingVsix: SideloadVsixEntry[] = []; + + for (const entry of entries) { + const id = typeof entry === 'string' ? entry : entry.id; + if (installedIds.has(id.toLowerCase())) { + continue; + } + if (typeof entry === 'string') { + missingGallery.push(entry); + } else { + missingVsix.push(entry); + } + } + + if (missingGallery.length === 0 && missingVsix.length === 0) { this.logService.info(`${TAG} All sideload extensions already installed`); return; } - this.logService.info(`${TAG} Installing ${missing.length} missing extension(s): ${missing.join(', ')}`); + await Promise.all([ + this.installFromGallery(missingGallery), + this.installFromVsix(missingVsix), + ]); + } + + private async installFromGallery(ids: string[]): Promise { + if (ids.length === 0) { + return; + } + + this.logService.info(`${TAG} Installing ${ids.length} extension(s) from gallery: ${ids.join(', ')}`); - // Check if gallery service is available if (!this.extensionGalleryService.isEnabled()) { - this.logService.warn(`${TAG} Extension gallery is not available — skipping sideload`); + this.logService.warn(`${TAG} Extension gallery is not available — skipping gallery installs`); return; } - // Resolve extension IDs to gallery entries const galleryExtensions = await this.extensionGalleryService.getExtensions( - missing.map(id => ({ id })), + ids.map(id => ({ id })), CancellationToken.None ); const resolved = new Map(galleryExtensions.map(ext => [ext.identifier.id.toLowerCase(), ext])); - for (const id of missing) { + for (const id of ids) { const galleryExt = resolved.get(id.toLowerCase()); if (!galleryExt) { this.logService.warn(`${TAG} Extension "${id}" not found in gallery — skipping`); @@ -90,4 +139,32 @@ export class CodexSideloaderContribution extends Disposable implements IWorkbenc } } } + + private async installFromVsix(entries: SideloadVsixEntry[]): Promise { + if (entries.length === 0) { + return; + } + + this.logService.info(`${TAG} Installing ${entries.length} extension(s) from VSIX: ${entries.map(e => e.id).join(', ')}`); + + // Use the shared process 'extensions' IPC channel to download via + // Node.js networking, bypassing renderer CORS restrictions on redirects. + const channel = this.sharedProcessService.getChannel('extensions'); + + for (const entry of entries) { + try { + await channel.call('install', [URI.parse(entry.vsix), { + installGivenVersion: true, + pinned: true, + }]); + this.logService.info(`${TAG} Installed "${entry.id}" from VSIX ${entry.vsix}`); + } catch (err) { + this.logService.error(`${TAG} Failed to install "${entry.id}" from VSIX ${entry.vsix}`, err); + this.notificationService.notify({ + severity: Severity.Warning, + message: `Codex: Failed to install extension "${entry.id}" from VSIX. It may be installed manually from the Extensions view.`, + }); + } + } + } } From 0e31ebe3254a990d9165b03274a5f02f82cbfe4f Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Thu, 2 Apr 2026 14:16:21 -0600 Subject: [PATCH 03/21] Fix build: cast through unknown, guard empty bundle array - Fix TS2352 by casting IProductService through unknown before indexing with a string key - Guard get-extensions.sh against empty bundle array (macOS seq 0 -1 outputs 0, causing a null download attempt) --- get-extensions.sh | 5 +++++ .../contrib/codexSideloader/browser/codexSideloader.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/get-extensions.sh b/get-extensions.sh index f6cf5454d27..193d1e987de 100755 --- a/get-extensions.sh +++ b/get-extensions.sh @@ -29,6 +29,11 @@ install_vsix() { count=$(jq -r '.bundle | length' "${BUNDLE_JSON}") +if [[ "${count}" -eq 0 ]]; then + echo "[get-extensions] No bundled extensions to download." + return 0 +fi + for i in $(seq 0 $((count - 1))); do name=$(jq -r ".bundle[$i].name" "${BUNDLE_JSON}") repo=$(jq -r ".bundle[$i].github_release" "${BUNDLE_JSON}") diff --git a/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts index 61dafaca9d4..dcbe6ee82cc 100644 --- a/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts +++ b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts @@ -55,7 +55,7 @@ export class CodexSideloaderContribution extends Disposable implements IWorkbenc ) { super(); - const configured = (this.productService as Record)['codexSideloadExtensions']; + const configured = (this.productService as unknown as Record)['codexSideloadExtensions']; if (!Array.isArray(configured) || configured.length === 0) { this.logService.info(`${TAG} No sideload extensions configured in product.json`); return; From aa626d25558bed3661a602d7565edd38d913098b Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Thu, 2 Apr 2026 14:44:25 -0600 Subject: [PATCH 04/21] Switch codex-editor and frontier-auth to pre-release VSIXs for pin beta Point sideloader at PR-built VSIXs for codex-editor (0.24.0-pr829) and frontier-authentication (0.4.24-pr23) to test pin enforcement. --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index fa195c4d3d6..36f291da645 100644 --- a/product.json +++ b/product.json @@ -600,9 +600,9 @@ } }, "codexSideloadExtensions": [ - "project-accelerate.codex-editor-extension", + { "id": "project-accelerate.codex-editor-extension", "vsix": "https://github.com/genesis-ai-dev/codex-editor/releases/download/0.24.0-pr829-96919c1a/codex-editor-extension-0.24.0-pr829-96919c1a.vsix" }, "project-accelerate.shared-state-store", "project-accelerate.vscode-edit-table", - "frontier-rnd.frontier-authentication" + { "id": "frontier-rnd.frontier-authentication", "vsix": "https://github.com/genesis-ai-dev/frontier-authentication/releases/download/0.4.24-pr23-7b219f0/frontier-authentication-0.4.24-pr23-7b219f0.vsix" } ] } From ecb2e809150eb7efc9c99eb35ba556ac11705c8a Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Thu, 2 Apr 2026 14:53:38 -0600 Subject: [PATCH 05/21] Add CodexSideloader to AGENTS.md, update extension bundling table --- AGENTS.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 90cd8823d58..cf20f74506b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -82,7 +82,7 @@ Extensions reach the final build three ways: |--------|--------|------| | **Built-in** (compiled from source) | `vscode/extensions/` | Compiled by gulp during build | | **Downloaded** (pre-built VSIX) | `bundle-extensions.json` | Downloaded from GitHub Releases by `get-extensions.sh` | -| **Sideloaded** (runtime install) | Extension sideloader config | Installed from OpenVSX on first launch | +| **Sideloaded** (runtime install) | `product.json` `codexSideloadExtensions` | Installed on first launch by `CodexSideloader` shell contribution (from gallery or direct VSIX URL) | ### Output @@ -179,6 +179,13 @@ Enforces project-scoped extension version pins. Reads `pinnedExtensions` from pr - **Loop Guard:** Includes a 3-cycle circuit breaker to prevent infinite reload loops if enforcement fails. - **Lifecycle Management:** Automatic cleanup of orphaned profiles every 14 days. +### CodexSideloader (Workbench Contribution) + +**Location:** `src/stable/src/vs/workbench/contrib/codexSideloader/` +**Patch:** `patches/feat-codex-sideloader.patch` (adds import to `workbench.common.main.ts`, depends on `feat-codex-conductor.patch`) + +Ensures global extensions are installed on first launch. Reads the `codexSideloadExtensions` array from `product.json`. Entries can be a string (gallery install from Open VSX) or an object with `id` + `vsix` fields (direct VSIX install via shared process IPC, bypassing the marketplace). Replaces the standalone `extension-sideloader` extension. + ### CLI Pin Commands (Rust) **Overlay:** `src/stable/cli/src/commands/pin.rs` From a0758635fa2ddee9c9d950d129d012316c41df1e Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Thu, 9 Apr 2026 11:14:28 -0600 Subject: [PATCH 06/21] bump extensions --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index 36f291da645..a7eab87d346 100644 --- a/product.json +++ b/product.json @@ -600,9 +600,9 @@ } }, "codexSideloadExtensions": [ - { "id": "project-accelerate.codex-editor-extension", "vsix": "https://github.com/genesis-ai-dev/codex-editor/releases/download/0.24.0-pr829-96919c1a/codex-editor-extension-0.24.0-pr829-96919c1a.vsix" }, + { "id": "project-accelerate.codex-editor-extension", "vsix": "https://github.com/genesis-ai-dev/codex-editor/releases/download/0.24.0-pr829-0b1c8f67/codex-editor-extension-0.24.0-pr829-0b1c8f67.vsix" }, "project-accelerate.shared-state-store", "project-accelerate.vscode-edit-table", - { "id": "frontier-rnd.frontier-authentication", "vsix": "https://github.com/genesis-ai-dev/frontier-authentication/releases/download/0.4.24-pr23-7b219f0/frontier-authentication-0.4.24-pr23-7b219f0.vsix" } + { "id": "frontier-rnd.frontier-authentication", "vsix": "https://github.com/genesis-ai-dev/frontier-authentication/releases/download/0.4.24-pr23-7669513/frontier-authentication-0.4.24-pr23-7669513.vsix" } ] } From 1810f3cee4bfab69c2faa2c469776ce1239d45f2 Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Fri, 10 Apr 2026 10:28:35 -0600 Subject: [PATCH 07/21] Version-aware VSIX sideload: reinstall on version mismatch Previously the sideloader only checked extension ID presence, so if codex-editor 0.24.0 was already installed it would skip the VSIX install even when a different version was configured. Add a required `version` field to VSIX entries and reinstall whenever the installed manifest version doesn't match. --- product.json | 4 ++-- .../browser/codexSideloader.ts | 21 ++++++++++++------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/product.json b/product.json index a7eab87d346..28afa7d8d89 100644 --- a/product.json +++ b/product.json @@ -600,9 +600,9 @@ } }, "codexSideloadExtensions": [ - { "id": "project-accelerate.codex-editor-extension", "vsix": "https://github.com/genesis-ai-dev/codex-editor/releases/download/0.24.0-pr829-0b1c8f67/codex-editor-extension-0.24.0-pr829-0b1c8f67.vsix" }, + { "id": "project-accelerate.codex-editor-extension", "vsix": "https://github.com/genesis-ai-dev/codex-editor/releases/download/0.24.0-pr829-b1e9d6f2/codex-editor-extension-0.24.0-pr829-b1e9d6f2.vsix", "version": "0.24.0-pr829-b1e9d6f2" }, "project-accelerate.shared-state-store", "project-accelerate.vscode-edit-table", - { "id": "frontier-rnd.frontier-authentication", "vsix": "https://github.com/genesis-ai-dev/frontier-authentication/releases/download/0.4.24-pr23-7669513/frontier-authentication-0.4.24-pr23-7669513.vsix" } + { "id": "frontier-rnd.frontier-authentication", "vsix": "https://github.com/genesis-ai-dev/frontier-authentication/releases/download/0.4.24-pr23-7669513/frontier-authentication-0.4.24-pr23-7669513.vsix", "version": "0.4.24-pr23-7669513" } ] } diff --git a/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts index dcbe6ee82cc..f5b3a67c10d 100644 --- a/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts +++ b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts @@ -21,6 +21,7 @@ const TAG = '[CodexSideloader]'; interface SideloadVsixEntry { id: string; vsix: string; + version: string; } type SideloadEntry = string | SideloadVsixEntry; @@ -33,7 +34,8 @@ function parseSideloadEntries(raw: unknown[]): SideloadEntry[] { } else if ( item && typeof item === 'object' && typeof (item as Record).id === 'string' && - typeof (item as Record).vsix === 'string' + typeof (item as Record).vsix === 'string' && + typeof (item as Record).version === 'string' ) { entries.push(item as SideloadVsixEntry); } @@ -73,20 +75,23 @@ export class CodexSideloaderContribution extends Disposable implements IWorkbenc private async ensureExtensions(entries: SideloadEntry[]): Promise { const installed = await this.extensionManagementService.getInstalled(ExtensionType.User); - const installedIds = new Set(installed.map(e => e.identifier.id.toLowerCase())); const missingGallery: string[] = []; const missingVsix: SideloadVsixEntry[] = []; for (const entry of entries) { - const id = typeof entry === 'string' ? entry : entry.id; - if (installedIds.has(id.toLowerCase())) { - continue; - } if (typeof entry === 'string') { - missingGallery.push(entry); + // Gallery entry: skip if ID is present (any version) + const found = installed.some(e => e.identifier.id.toLowerCase() === entry.toLowerCase()); + if (!found) { + missingGallery.push(entry); + } } else { - missingVsix.push(entry); + // VSIX entry: skip only if ID AND version match + const installedExt = installed.find(e => e.identifier.id.toLowerCase() === entry.id.toLowerCase()); + if (!installedExt || installedExt.manifest.version !== entry.version) { + missingVsix.push(entry); + } } } From e637b620a7064764abc1a6ebd621442e0614e9a9 Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Fri, 10 Apr 2026 10:30:42 -0600 Subject: [PATCH 08/21] Make prepare_assets.sh safe to run locally without CI env - Default all SHOULD_BUILD_* toggles and macOS signing env vars so sourcing doesn't crash under `set -e` when run outside CI - Build unsigned DMG with a warning when no Developer ID cert is configured, instead of silently skipping - Fix typo: dev/build.sh sourced macos-codesign.env but the file is actually codesign.env --- dev/build.sh | 2 +- prepare_assets.sh | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/dev/build.sh b/dev/build.sh index 7e2fb3b50be..78204635c26 100755 --- a/dev/build.sh +++ b/dev/build.sh @@ -158,7 +158,7 @@ if [[ "${SKIP_ASSETS}" == "no" ]]; then fi if [[ "${OS_NAME}" == "osx" && -f "dev/osx/codesign.env" ]]; then - . dev/osx/macos-codesign.env + . dev/osx/codesign.env echo "CERTIFICATE_OSX_ID: ${CERTIFICATE_OSX_ID}" fi diff --git a/prepare_assets.sh b/prepare_assets.sh index 269cefac265..ef05ef02137 100755 --- a/prepare_assets.sh +++ b/prepare_assets.sh @@ -4,6 +4,30 @@ set -e APP_NAME_LC="$( echo "${APP_NAME}" | awk '{print tolower($0)}' )" +YELLOW=$'\033[33m' +RESET=$'\033[0m' + +# Local dev packaging often runs without CI-exported asset toggles or macOS +# signing secrets. Default optional inputs so sourcing this script remains safe. +CERTIFICATE_OSX_P12_DATA="${CERTIFICATE_OSX_P12_DATA:-}" +CERTIFICATE_OSX_P12_PASSWORD="${CERTIFICATE_OSX_P12_PASSWORD:-}" +CERTIFICATE_OSX_ID="${CERTIFICATE_OSX_ID:-}" +CERTIFICATE_OSX_TEAM_ID="${CERTIFICATE_OSX_TEAM_ID:-}" +CERTIFICATE_OSX_APP_PASSWORD="${CERTIFICATE_OSX_APP_PASSWORD:-}" +SHOULD_BUILD_ZIP="${SHOULD_BUILD_ZIP:-yes}" +SHOULD_BUILD_DMG="${SHOULD_BUILD_DMG:-yes}" +SHOULD_BUILD_SRC="${SHOULD_BUILD_SRC:-no}" +SHOULD_BUILD_TAR="${SHOULD_BUILD_TAR:-yes}" +SHOULD_BUILD_DEB="${SHOULD_BUILD_DEB:-yes}" +SHOULD_BUILD_RPM="${SHOULD_BUILD_RPM:-yes}" +SHOULD_BUILD_APPIMAGE="${SHOULD_BUILD_APPIMAGE:-yes}" +SHOULD_BUILD_EXE_SYS="${SHOULD_BUILD_EXE_SYS:-yes}" +SHOULD_BUILD_EXE_USR="${SHOULD_BUILD_EXE_USR:-yes}" +SHOULD_BUILD_MSI="${SHOULD_BUILD_MSI:-yes}" +SHOULD_BUILD_MSI_NOUP="${SHOULD_BUILD_MSI_NOUP:-yes}" +SHOULD_BUILD_REH="${SHOULD_BUILD_REH:-no}" +SHOULD_BUILD_REH_WEB="${SHOULD_BUILD_REH_WEB:-no}" +SHOULD_BUILD_CLI="${SHOULD_BUILD_CLI:-yes}" mkdir -p assets @@ -71,9 +95,12 @@ if [[ "${OS_NAME}" == "osx" ]]; then cd .. fi - if [[ -n "${CERTIFICATE_OSX_P12_DATA}" && "${SHOULD_BUILD_DMG}" != "no" ]]; then + if [[ "${SHOULD_BUILD_DMG}" != "no" ]]; then echo "Building and moving DMG" pushd "VSCode-darwin-${VSCODE_ARCH}" + if [[ -z "${CERTIFICATE_OSX_P12_DATA}" ]]; then + printf '%s\n' "${YELLOW}Warning: generating an unsigned macOS DMG because no Developer ID signing certificate is configured. Team members may see Gatekeeper warnings when opening it.${RESET}" + fi npx create-dmg ./*.app . mv ./*.dmg "../assets/${APP_NAME}.${VSCODE_ARCH}.${RELEASE_VERSION}.dmg" popd From cbf777c9eda7da3083d4a21da3d33bc89efac0c5 Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Sat, 11 Apr 2026 11:17:04 -0600 Subject: [PATCH 09/21] only sideload on default profile --- .../codexSideloader/browser/codexSideloader.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts index f5b3a67c10d..e2576ddc25d 100644 --- a/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts +++ b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts @@ -11,6 +11,7 @@ import { IProductService } from '../../../../platform/product/common/productServ import { ILogService } from '../../../../platform/log/common/log.js'; import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; import { ISharedProcessService } from '../../../../platform/ipc/electron-browser/services.js'; +import { IUserDataProfileService } from '../../../services/userDataProfile/common/userDataProfile.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { ExtensionType } from '../../../../platform/extensions/common/extensions.js'; import { URI } from '../../../../base/common/uri.js'; @@ -54,9 +55,20 @@ export class CodexSideloaderContribution extends Disposable implements IWorkbenc @ILogService private readonly logService: ILogService, @INotificationService private readonly notificationService: INotificationService, @ISharedProcessService private readonly sharedProcessService: ISharedProcessService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, ) { super(); + // Only run sideload in the default profile. CodexConductor owns + // extension management inside pin profiles (via explicit + // profileLocation); the shared-process 'extensions' install channel + // defaults to the default profile when no profileLocation is passed, + // so running the sideloader in a pin-profile window would silently + // and pointlessly change the default profile. + if (!this.userDataProfileService.currentProfile.isDefault) { + return; + } + const configured = (this.productService as unknown as Record)['codexSideloadExtensions']; if (!Array.isArray(configured) || configured.length === 0) { this.logService.info(`${TAG} No sideload extensions configured in product.json`); From d6f7f331b919f92f77fa8c286329b7593cb9f0e0 Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Sat, 11 Apr 2026 12:15:03 -0600 Subject: [PATCH 10/21] bump version --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 28afa7d8d89..5a1a03f725b 100644 --- a/product.json +++ b/product.json @@ -600,7 +600,7 @@ } }, "codexSideloadExtensions": [ - { "id": "project-accelerate.codex-editor-extension", "vsix": "https://github.com/genesis-ai-dev/codex-editor/releases/download/0.24.0-pr829-b1e9d6f2/codex-editor-extension-0.24.0-pr829-b1e9d6f2.vsix", "version": "0.24.0-pr829-b1e9d6f2" }, + { "id": "project-accelerate.codex-editor-extension", "vsix": "https://github.com/genesis-ai-dev/codex-editor/releases/download/0.24.0-pr856-076389d4/codex-editor-extension-0.24.0-pr856-076389d4.vsix", "version": "0.24.0-pr856-076389d4" }, "project-accelerate.shared-state-store", "project-accelerate.vscode-edit-table", { "id": "frontier-rnd.frontier-authentication", "vsix": "https://github.com/genesis-ai-dev/frontier-authentication/releases/download/0.4.24-pr23-7669513/frontier-authentication-0.4.24-pr23-7669513.vsix", "version": "0.4.24-pr23-7669513" } From 6ccee2e03d80bcdb0ab83ef705f5d4816e46b150 Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Mon, 13 Apr 2026 12:00:44 -0600 Subject: [PATCH 11/21] feat: add create-pr-release script and support for Codex Beta branding --- create-pr-release | 93 +++++++++++++++++++++++++++++++++++++++++++++++ dev/build.sh | 10 ++--- get_repo.sh | 4 +- prepare_vscode.sh | 18 ++++----- 4 files changed, 109 insertions(+), 16 deletions(-) create mode 100755 create-pr-release diff --git a/create-pr-release b/create-pr-release new file mode 100755 index 00000000000..46c8b96a76e --- /dev/null +++ b/create-pr-release @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Ensure we're in the right directory +if [[ ! -f product.json ]]; then + echo "Error: no product.json in current directory. Run this from the root of the codex repository." >&2 + exit 1 +fi + +if [[ -n "$(git status --porcelain 2>/dev/null)" ]]; then + printf '\033[33mWarning: git working tree is dirty.\033[0m\n' >&2 +fi + +# --------------------------------------------------------------------------- +# Resolve VERSION +# --------------------------------------------------------------------------- +# Get the base version from upstream/stable.json (the MS tag) +MS_TAG=$(jq -r '.tag' "./upstream/stable.json") +SHORT_HASH="$(git rev-parse --short HEAD)" + +# Try to get PR number from current branch or gh +if ! command -v gh &>/dev/null; then + echo "Error: 'gh' CLI is required." >&2 + exit 1 +fi + +PR_NUMBER="$(gh pr view --json number -q .number 2>/dev/null || true)" + +if [[ -z "$PR_NUMBER" ]]; then + echo "No open PR detected for the current branch." + # Use current date as a fallback for versioning if no PR + VERSION="${MS_TAG}-dev-${SHORT_HASH}" +else + VERSION="${MS_TAG}-pr${PR_NUMBER}-${SHORT_HASH}" +fi + +echo "Generated version: $VERSION" + +# --------------------------------------------------------------------------- +# Setup environment variables +# --------------------------------------------------------------------------- +export APP_NAME="Codex Beta" +export BINARY_NAME="codex-beta" +export RELEASE_VERSION="${VERSION}" +export CUSTOM_RELEASE_VERSION="${VERSION}" # Hook for get_repo.sh +export SKIP_SOURCE="no" +export SKIP_ASSETS="no" # We want the DMG +export VSCODE_QUALITY="stable" + +echo "==> Starting build for $APP_NAME ($VERSION)" +# dev/build.sh is the main entry point for local builds +./dev/build.sh -p + +# --------------------------------------------------------------------------- +# Release on GitHub +# --------------------------------------------------------------------------- + +# Determine architecture +UNAME_ARCH=$( uname -m ) +if [[ "${UNAME_ARCH}" == "aarch64" || "${UNAME_ARCH}" == "arm64" ]]; then + ARCH="arm64" +else + ARCH="x64" +fi + +# The filename is constructed in prepare_assets.sh +DMG_FILE="assets/${APP_NAME}.${ARCH}.${VERSION}.dmg" + +if [[ ! -f "$DMG_FILE" ]]; then + echo "Error: DMG artifact not found at $DMG_FILE" >&2 + echo "Listing assets directory:" + ls -l assets/ + exit 1 +fi + +echo "==> Creating GitHub prerelease: $VERSION" +gh release create "$VERSION" \ + --target "$(git rev-parse HEAD)" \ + --prerelease \ + --title "$APP_NAME $VERSION" \ + --generate-notes \ + "./$DMG_FILE" + +REPO_URL="$(gh repo view --json url -q .url)" +RELEASE_URL="${REPO_URL}/releases/tag/${VERSION}" +echo "Done! Prerelease $VERSION created with $DMG_FILE" + +if [[ -n "${PR_NUMBER:-}" ]]; then + gh pr comment "$PR_NUMBER" --body "Pre-release: ${VERSION} ${RELEASE_URL}" + echo "Commented PR #${PR_NUMBER}" +fi + +open "$REPO_URL/releases" diff --git a/dev/build.sh b/dev/build.sh index 78204635c26..bb86b6a65d2 100755 --- a/dev/build.sh +++ b/dev/build.sh @@ -5,12 +5,12 @@ # to run with Bash: "C:\Program Files\Git\bin\bash.exe" ./dev/build.sh ### -export APP_NAME="Codex" -export ASSETS_REPOSITORY="BiblioNexus-Foundation/codex" -export BINARY_NAME="codex" +export APP_NAME="${APP_NAME:-Codex}" +export ASSETS_REPOSITORY="${ASSETS_REPOSITORY:-BiblioNexus-Foundation/codex}" +export BINARY_NAME="${BINARY_NAME:-codex}" export CI_BUILD="no" -export GH_REPO_PATH="genesis-ai-dev/codex" -export ORG_NAME="Codex" +export GH_REPO_PATH="${GH_REPO_PATH:-genesis-ai-dev/codex}" +export ORG_NAME="${ORG_NAME:-Codex}" export SHOULD_BUILD="yes" export SKIP_ASSETS="yes" export SHOULD_BUILD_REH="no" diff --git a/get_repo.sh b/get_repo.sh index f2e03d50200..30c18c4939f 100755 --- a/get_repo.sh +++ b/get_repo.sh @@ -42,14 +42,14 @@ if [[ -z "${RELEASE_VERSION}" ]]; then fi else if [[ "${VSCODE_QUALITY}" == "insider" ]]; then - if [[ "${RELEASE_VERSION}" =~ ^([0-9]+\.[0-9]+\.[0-5])[0-9]+-insider$ ]]; then + if [[ "${RELEASE_VERSION}" =~ ^([0-9]+\.[0-9]+\.[0-9]+) ]]; then MS_TAG="${BASH_REMATCH[1]}" else echo "Error: Bad RELEASE_VERSION: ${RELEASE_VERSION}" exit 1 fi else - if [[ "${RELEASE_VERSION}" =~ ^([0-9]+\.[0-9]+\.[0-5])[0-9]+$ ]]; then + if [[ "${RELEASE_VERSION}" =~ ^([0-9]+\.[0-9]+\.[0-9]+) ]]; then MS_TAG="${BASH_REMATCH[1]}" else echo "Error: Bad RELEASE_VERSION: ${RELEASE_VERSION}" diff --git a/prepare_vscode.sh b/prepare_vscode.sh index 5d1f7890546..fab809f9160 100755 --- a/prepare_vscode.sh +++ b/prepare_vscode.sh @@ -90,16 +90,16 @@ if [[ "${VSCODE_QUALITY}" == "insider" ]]; then setpath "product" "win32ContextMenu.x64.clsid" "90AAD229-85FD-43A3-B82D-8598A88829CF" setpath "product" "win32ContextMenu.arm64.clsid" "7544C31C-BDBF-4DDF-B15E-F73A46D6723D" else - setpath "product" "nameShort" "Codex" - setpath "product" "nameLong" "Codex" - setpath "product" "applicationName" "codex" - setpath "product" "linuxIconName" "codex" + setpath "product" "nameShort" "${APP_NAME}" + setpath "product" "nameLong" "${APP_NAME}" + setpath "product" "applicationName" "${BINARY_NAME}" + setpath "product" "linuxIconName" "${BINARY_NAME}" setpath "product" "quality" "stable" - setpath "product" "dataFolderName" ".codex" - setpath "product" "urlProtocol" "codex" - setpath "product" "serverApplicationName" "codex-server" - setpath "product" "serverDataFolderName" ".codex-server" - setpath "product" "darwinBundleIdentifier" "com.codex" + setpath "product" "dataFolderName" ".${BINARY_NAME}" + setpath "product" "urlProtocol" "${BINARY_NAME}" + setpath "product" "serverApplicationName" "${BINARY_NAME}-server" + setpath "product" "serverDataFolderName" ".${BINARY_NAME}-server" + setpath "product" "darwinBundleIdentifier" "com.${BINARY_NAME}" setpath "product" "win32AppUserModelId" "Codex.Codex" setpath "product" "win32DirName" "Codex" setpath "product" "win32MutexName" "codex" From 23260f71e00a2938c2b4871f71f969bc3ca2ab96 Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Mon, 13 Apr 2026 12:00:59 -0600 Subject: [PATCH 12/21] version bump --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index 5a1a03f725b..60a0b4ad33e 100644 --- a/product.json +++ b/product.json @@ -600,9 +600,9 @@ } }, "codexSideloadExtensions": [ - { "id": "project-accelerate.codex-editor-extension", "vsix": "https://github.com/genesis-ai-dev/codex-editor/releases/download/0.24.0-pr856-076389d4/codex-editor-extension-0.24.0-pr856-076389d4.vsix", "version": "0.24.0-pr856-076389d4" }, + { "id": "project-accelerate.codex-editor-extension", "vsix": "https://github.com/genesis-ai-dev/codex-editor/releases/tag/0.24.0-pr856-d50039fa", "version": "0.24.0-pr856-d50039fa" }, "project-accelerate.shared-state-store", "project-accelerate.vscode-edit-table", - { "id": "frontier-rnd.frontier-authentication", "vsix": "https://github.com/genesis-ai-dev/frontier-authentication/releases/download/0.4.24-pr23-7669513/frontier-authentication-0.4.24-pr23-7669513.vsix", "version": "0.4.24-pr23-7669513" } + { "id": "frontier-rnd.frontier-authentication", "vsix": "https://github.com/genesis-ai-dev/frontier-authentication/releases/tag/0.4.24-pr25-365a7e6", "version": "0.4.24-pr25-365a7e6" } ] } From 6446b3fb64a0b95f5c472a04727f11144203ee6d Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Mon, 13 Apr 2026 16:34:15 -0600 Subject: [PATCH 13/21] docs: update CodexSideloader entry for version-aware VSIX reinstall - Add version field to object entry format in CodexSideloader description - Clarify that string entries skip any installed version; VSIX entries reinstall on version mismatch - Add feat-codex-sideloader.patch to Patch Dependencies table --- AGENTS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index cf20f74506b..ba4a0e22cb7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -159,6 +159,7 @@ Some Codex patches modify files that earlier patches also touch. When this happe | Patch | Depends on | |-------|-----------| | `feat-cli-pinning.patch` | `binary-name.patch` (both modify `nativeHostMainService.ts`) | +| `feat-codex-sideloader.patch` | `feat-codex-conductor.patch` (both add imports to `workbench.common.main.ts`) | If a patch fails to apply with "patch does not apply", check whether a prerequisite patch changed the same file. Regenerate using `dev/patch.sh` with the prerequisite listed first. @@ -184,7 +185,7 @@ Enforces project-scoped extension version pins. Reads `pinnedExtensions` from pr **Location:** `src/stable/src/vs/workbench/contrib/codexSideloader/` **Patch:** `patches/feat-codex-sideloader.patch` (adds import to `workbench.common.main.ts`, depends on `feat-codex-conductor.patch`) -Ensures global extensions are installed on first launch. Reads the `codexSideloadExtensions` array from `product.json`. Entries can be a string (gallery install from Open VSX) or an object with `id` + `vsix` fields (direct VSIX install via shared process IPC, bypassing the marketplace). Replaces the standalone `extension-sideloader` extension. +Ensures global extensions are installed on startup. Reads the `codexSideloadExtensions` array from `product.json`. Entries can be a string (gallery install from Open VSX) or an object with `id`, `vsix`, and `version` fields (direct VSIX install via shared process IPC, bypassing the marketplace). String entries are skipped if the extension is already installed at any version; object entries are reinstalled whenever the installed version doesn't match `version`. Replaces the standalone `extension-sideloader` extension. ### CLI Pin Commands (Rust) From 7501bc00372a29c19b535bac9b6a0045f05eece6 Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Mon, 13 Apr 2026 16:22:29 -0600 Subject: [PATCH 14/21] continue if dmg is created even though npx fails due to other reasons --- create-pr-release | 2 +- prepare_assets.sh | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/create-pr-release b/create-pr-release index 46c8b96a76e..3cabcde6dfa 100755 --- a/create-pr-release +++ b/create-pr-release @@ -49,7 +49,7 @@ export VSCODE_QUALITY="stable" echo "==> Starting build for $APP_NAME ($VERSION)" # dev/build.sh is the main entry point for local builds -./dev/build.sh -p +./dev/build.sh -sp # --------------------------------------------------------------------------- # Release on GitHub diff --git a/prepare_assets.sh b/prepare_assets.sh index ef05ef02137..dffb8fd806e 100755 --- a/prepare_assets.sh +++ b/prepare_assets.sh @@ -101,7 +101,11 @@ if [[ "${OS_NAME}" == "osx" ]]; then if [[ -z "${CERTIFICATE_OSX_P12_DATA}" ]]; then printf '%s\n' "${YELLOW}Warning: generating an unsigned macOS DMG because no Developer ID signing certificate is configured. Team members may see Gatekeeper warnings when opening it.${RESET}" fi - npx create-dmg ./*.app . + npx create-dmg ./*.app . || true + if ! ls ./*.dmg 1>/dev/null 2>&1; then + echo "Error: DMG creation failed — no .dmg file was produced" >&2 + exit 1 + fi mv ./*.dmg "../assets/${APP_NAME}.${VSCODE_ARCH}.${RELEASE_VERSION}.dmg" popd fi From d7fe2ff6b5c967772c01ba40ac685d8213c2acde Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Mon, 13 Apr 2026 16:25:47 -0600 Subject: [PATCH 15/21] fix ext links --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index 60a0b4ad33e..277e4e088af 100644 --- a/product.json +++ b/product.json @@ -600,9 +600,9 @@ } }, "codexSideloadExtensions": [ - { "id": "project-accelerate.codex-editor-extension", "vsix": "https://github.com/genesis-ai-dev/codex-editor/releases/tag/0.24.0-pr856-d50039fa", "version": "0.24.0-pr856-d50039fa" }, + { "id": "project-accelerate.codex-editor-extension", "vsix": "https://github.com/genesis-ai-dev/codex-editor/releases/download/0.24.0-pr856-d50039fa/codex-editor-extension-0.24.0-pr856-d50039fa.vsix", "version": "0.24.0-pr856-d50039fa" }, "project-accelerate.shared-state-store", "project-accelerate.vscode-edit-table", - { "id": "frontier-rnd.frontier-authentication", "vsix": "https://github.com/genesis-ai-dev/frontier-authentication/releases/tag/0.4.24-pr25-365a7e6", "version": "0.4.24-pr25-365a7e6" } + { "id": "frontier-rnd.frontier-authentication", "vsix": "https://github.com/genesis-ai-dev/frontier-authentication/releases/download/0.4.24-pr25-365a7e6/frontier-authentication-0.4.24-pr25-365a7e6.vsix", "version": "0.4.24-pr25-365a7e6" } ] } From 33383aa1f7fae9236c8af77ccc387ac19cbf549c Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Tue, 14 Apr 2026 11:45:58 -0600 Subject: [PATCH 16/21] Add PR build workflow triggered by /build comment --- .github/workflows/patch-rebuild.yml | 125 +------------ .github/workflows/pr-build.yml | 275 ++++++++++++++++++++++++++++ 2 files changed, 280 insertions(+), 120 deletions(-) create mode 100644 .github/workflows/pr-build.yml diff --git a/.github/workflows/patch-rebuild.yml b/.github/workflows/patch-rebuild.yml index 22d02371c3c..d66cb70967e 100644 --- a/.github/workflows/patch-rebuild.yml +++ b/.github/workflows/patch-rebuild.yml @@ -2,125 +2,10 @@ name: Patch Rebuild (Force Build) on: workflow_dispatch: - inputs: - quality: - description: "Build quality" - required: true - default: "stable" - type: choice - options: - - stable - - insider - reason: - description: 'Reason for rebuild (e.g., "Fix microphone patch", "Add new feature")' - required: true - type: string - -env: - APP_NAME: Codex - GH_REPO_PATH: ${{ github.repository }} - ORG_NAME: ${{ github.repository_owner }} jobs: - prepare: - runs-on: ubuntu-latest - outputs: - ms_commit: ${{ steps.prepare.outputs.ms_commit }} - ms_tag: ${{ steps.prepare.outputs.ms_tag }} - release_version: ${{ steps.prepare.outputs.release_version }} - build_reason: ${{ steps.prepare.outputs.build_reason }} - - steps: - - uses: actions/checkout@v4 - with: - token: ${{ secrets.STRONGER_GITHUB_TOKEN }} - - - name: Prepare patch rebuild - id: prepare - env: - VSCODE_QUALITY: ${{ github.event.inputs.quality }} - BUILD_REASON: ${{ github.event.inputs.reason }} - run: | - echo "=== Patch Rebuild for ${VSCODE_QUALITY} ===" - echo "Reason: ${BUILD_REASON}" - - # Get current version from upstream file - if [[ ! -f "./upstream/${VSCODE_QUALITY}.json" ]]; then - echo "Error: No upstream/${VSCODE_QUALITY}.json found" - exit 1 - fi - - MS_COMMIT=$( jq -r '.commit' "./upstream/${VSCODE_QUALITY}.json" ) - MS_TAG=$( jq -r '.tag' "./upstream/${VSCODE_QUALITY}.json" ) - - echo "Current VS Code base: ${MS_TAG} (${MS_COMMIT})" - echo "ms_tag=${MS_TAG}" >> $GITHUB_OUTPUT - echo "ms_commit=${MS_COMMIT}" >> $GITHUB_OUTPUT - - # Generate unique build version with timestamp - # Use same format as normal builds - Julian day calculation ensures later builds have higher versions - # Format: MS_TAG + (Julian day * 24 + hour) = 1.99.24260 - # Since patch rebuilds happen AFTER original builds, they naturally get higher version numbers - # Note that a patch rebuild *could* be higher than an upstream vscodium build version, so it may not trigger an update notice if we have already patched more recently - TIME_PATCH=$(printf "%04d" $(($(date +%-j) * 24 + $(date +%-H)))) - - if [[ "${VSCODE_QUALITY}" == "insider" ]]; then - RELEASE_VERSION="${MS_TAG}${TIME_PATCH}-insider" - else - RELEASE_VERSION="${MS_TAG}${TIME_PATCH}" - fi - - echo "Generated rebuild version: ${RELEASE_VERSION}" - echo "release_version=${RELEASE_VERSION}" >> $GITHUB_OUTPUT - echo "build_reason=${BUILD_REASON}" >> $GITHUB_OUTPUT - - # Create a patch rebuild marker - echo "=== PATCH REBUILD ===" > PATCH_REBUILD_INFO.md - echo "**Build Version:** ${RELEASE_VERSION}" >> PATCH_REBUILD_INFO.md - echo "**Base VS Code:** ${MS_TAG}" >> PATCH_REBUILD_INFO.md - echo "**Rebuild Reason:** ${BUILD_REASON}" >> PATCH_REBUILD_INFO.md - echo "**Build Date:** $(date)" >> PATCH_REBUILD_INFO.md - echo "**Commit:** ${{ github.sha }}" >> PATCH_REBUILD_INFO.md - - # Commit build info for tracking - git config user.name "GitHub Actions" - git config user.email "actions@github.com" - git add PATCH_REBUILD_INFO.md - git commit -m "Patch rebuild: ${BUILD_REASON} (${RELEASE_VERSION})" || echo "No changes to commit" - git push || echo "No changes to push" - - trigger-all-builds: - needs: prepare - runs-on: ubuntu-latest - - steps: - - name: Trigger all platform builds - env: - GITHUB_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} - QUALITY: ${{ github.event.inputs.quality }} - RELEASE_VERSION: ${{ needs.prepare.outputs.release_version }} - BUILD_REASON: ${{ needs.prepare.outputs.build_reason }} - run: | - echo "🚀 Triggering PATCH REBUILD for all platforms" - echo "Version: ${RELEASE_VERSION}" - echo "Reason: ${BUILD_REASON}" - - # Force build by using repository dispatch with special payload - # This single dispatch will trigger all OS workflows that listen for this quality - curl -X POST \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer ${GITHUB_TOKEN}" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/repos/${{ github.repository }}/dispatches \ - -d "{ - \"event_type\": \"${QUALITY}\", - \"client_payload\": { - \"quality\": \"${QUALITY}\", - \"patch_rebuild\": true, - \"force_build\": true, - \"build_reason\": \"${BUILD_REASON}\", - \"release_version\": \"${RELEASE_VERSION}\" - } - }" - - echo "✅ Triggered all ${QUALITY} platform builds" + pr-build: + uses: genesis-ai-dev/codex/.github/workflows/pr-build.yml@feat/sideloader + with: + pr_number: '31' + secrets: inherit diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml new file mode 100644 index 00000000000..2e7d5ab65c7 --- /dev/null +++ b/.github/workflows/pr-build.yml @@ -0,0 +1,275 @@ +name: PR Build + +on: + issue_comment: + types: [created] + workflow_call: + inputs: + pr_number: + type: string + required: true + +jobs: + prepare: + if: | + github.event_name != 'issue_comment' || + (github.event.issue.pull_request != null && + github.event.comment.body == '/build' && + (github.event.comment.author_association == 'OWNER' || + github.event.comment.author_association == 'MEMBER' || + github.event.comment.author_association == 'COLLABORATOR')) + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} + head_sha: ${{ steps.pr.outputs.head_sha }} + pr_number: ${{ steps.pr.outputs.pr_number }} + + steps: + - name: Get PR head SHA + id: pr + uses: actions/github-script@v7 + with: + script: | + const pr_number = context.eventName === 'issue_comment' + ? context.issue.number + : Number('${{ inputs.pr_number }}'); + const pr = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr_number, + }); + core.setOutput('head_sha', pr.data.head.sha); + core.setOutput('pr_number', String(pr_number)); + + - name: React to comment + if: github.event_name == 'issue_comment' + uses: actions/github-script@v7 + with: + script: | + github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: 'rocket', + }); + + - uses: actions/checkout@v4 + with: + ref: ${{ steps.pr.outputs.head_sha }} + + - name: Compute version + id: version + run: | + MS_TAG=$(jq -r '.tag' ./upstream/stable.json) + SHORT_HASH=$(git rev-parse --short HEAD) + echo "version=${MS_TAG}-pr${{ steps.pr.outputs.pr_number }}-${SHORT_HASH}" >> "$GITHUB_OUTPUT" + + build-macos: + needs: prepare + runs-on: macos-14 + env: + APP_NAME: Codex Beta + BINARY_NAME: codex-beta + RELEASE_VERSION: ${{ needs.prepare.outputs.version }} + CUSTOM_RELEASE_VERSION: ${{ needs.prepare.outputs.version }} + VSCODE_QUALITY: stable + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.prepare.outputs.head_sha }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + + - name: Build + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CERTIFICATE_OSX_APP_PASSWORD: ${{ secrets.CERTIFICATE_OSX_APP_PASSWORD }} + CERTIFICATE_OSX_ID: ${{ secrets.CERTIFICATE_OSX_ID }} + CERTIFICATE_OSX_P12_DATA: ${{ secrets.CERTIFICATE_OSX_P12 }} + CERTIFICATE_OSX_P12_PASSWORD: ${{ secrets.CERTIFICATE_OSX_P12_PASSWORD }} + CERTIFICATE_OSX_TEAM_ID: ${{ secrets.CERTIFICATE_OSX_TEAM_ID }} + run: ./dev/build.sh -p + + - name: Upload DMG + uses: actions/upload-artifact@v4 + with: + name: macos-arm64 + path: assets/*.dmg + retention-days: 3 + + # Windows build is disabled pending fixes to the cross-compile pipeline. + # + # Root causes identified so far (both fixed in env vars but not yet validated): + # + # 1. OS_NAME not set → prepare_vscode.sh evaluates "../patches/${OS_NAME}/" as + # "../patches//" which matches the root patches dir, causing all patches to be + # applied twice. The second application of add-remote-url.patch (and others) + # fails because the files were already modified. Fix: OS_NAME: windows. + # + # 2. CI_BUILD not set → get-extensions.sh sources `set -euo pipefail` into the + # calling shell, enabling nounset (-u). build.sh line 44 then hits + # "${CI_BUILD}: unbound variable". Fix: CI_BUILD: 'yes' (also correctly + # skips local packaging, which is handled by the separate build-windows job). + # + # After those two env vars are confirmed working, the next unknown is whether + # build.sh completes compilation cleanly and produces a valid vscode artifact + # that the build-windows packaging job can consume. + # + # compile-windows: + # needs: prepare + # runs-on: ubuntu-22.04 + # env: + # APP_NAME: Codex Beta + # BINARY_NAME: codex-beta + # RELEASE_VERSION: ${{ needs.prepare.outputs.version }} + # CUSTOM_RELEASE_VERSION: ${{ needs.prepare.outputs.version }} + # VSCODE_ARCH: x64 + # VSCODE_QUALITY: stable + # CI_BUILD: 'yes' + # OS_NAME: windows + # SHOULD_BUILD: 'yes' + # SHOULD_BUILD_REH: 'no' + # SHOULD_BUILD_REH_WEB: 'no' + # + # steps: + # - uses: actions/checkout@v4 + # with: + # ref: ${{ needs.prepare.outputs.head_sha }} + # + # - name: Setup GCC + # uses: egor-tensin/setup-gcc@v1 + # with: + # version: 10 + # platform: x64 + # + # - name: Setup Node.js + # uses: actions/setup-node@v4 + # with: + # node-version-file: '.nvmrc' + # + # - name: Setup Python 3 + # uses: actions/setup-python@v5 + # with: + # python-version: '3.11' + # + # - name: Install libkrb5-dev + # run: sudo apt-get update -y && sudo apt-get install -y libkrb5-dev + # + # - name: Clone VSCode repo + # run: ./get_repo.sh + # + # - name: Build + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # run: ./build.sh + # + # - name: Compress vscode artifact + # run: | + # find vscode -type f \ + # -not -path "*/node_modules/*" \ + # -not -path "vscode/.build/node/*" \ + # -not -path "vscode/.git/*" > vscode.txt + # [ -d "vscode/.build/extensions/node_modules" ] && echo "vscode/.build/extensions/node_modules" >> vscode.txt + # echo "vscode/.git" >> vscode.txt + # tar -czf vscode.tar.gz -T vscode.txt + # + # - name: Upload vscode artifact + # uses: actions/upload-artifact@v4 + # with: + # name: vscode-compiled + # path: ./vscode.tar.gz + # retention-days: 1 + # + # build-windows: + # needs: [prepare, compile-windows] + # runs-on: windows-2022 + # defaults: + # run: + # shell: bash + # env: + # APP_NAME: Codex Beta + # BINARY_NAME: codex-beta + # RELEASE_VERSION: ${{ needs.prepare.outputs.version }} + # CUSTOM_RELEASE_VERSION: ${{ needs.prepare.outputs.version }} + # VSCODE_ARCH: x64 + # VSCODE_QUALITY: stable + # + # steps: + # - uses: actions/checkout@v4 + # with: + # ref: ${{ needs.prepare.outputs.head_sha }} + # + # - name: Setup Node.js + # uses: actions/setup-node@v4 + # with: + # node-version-file: '.nvmrc' + # + # - name: Setup Python 3 + # uses: actions/setup-python@v5 + # with: + # python-version: '3.11' + # + # - name: Download compiled vscode + # uses: actions/download-artifact@v4 + # with: + # name: vscode-compiled + # + # - name: Extract vscode artifact + # run: tar -xzf vscode.tar.gz + # + # - name: Build Windows package + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # npm_config_arch: x64 + # npm_config_target_arch: x64 + # run: ./build/windows/package.sh + # + # - name: Prepare assets + # run: ./prepare_assets.sh + # + # - name: Upload Windows artifacts + # uses: actions/upload-artifact@v4 + # with: + # name: windows-x64 + # path: | + # assets/*.exe + # assets/*.msi + # retention-days: 3 + + release: + needs: [prepare, build-macos] + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.STRONGER_GITHUB_TOKEN }} + VERSION: ${{ needs.prepare.outputs.version }} + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.prepare.outputs.head_sha }} + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts/ + + - name: Create prerelease + run: | + gh release create "$VERSION" \ + --target "${{ needs.prepare.outputs.head_sha }}" \ + --prerelease \ + --title "Codex Beta $VERSION" \ + --generate-notes \ + artifacts/macos-arm64/*.dmg + + - name: Comment on PR + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + RELEASE_URL="https://github.com/${{ github.repository }}/releases/tag/${VERSION}" + gh pr comment "${{ needs.prepare.outputs.pr_number }}" \ + --body "Pre-release build ready: [$VERSION]($RELEASE_URL)" From b8b25f7b61b143b158560fa58c65dafb58186464 Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Tue, 14 Apr 2026 11:48:15 -0600 Subject: [PATCH 17/21] fix(sideloader): install VSIX extensions to global location so they're visible in all profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, VSIX entries (e.g. Frontier) were installed via the raw shared-process channel without a profileLocation, which defaults to a profile-specific folder rather than the global extensions location. This made them invisible in pin profiles created by CodexConductor. Gallery entries use IWorkbenchExtensionManagementService.installFromGallery() which targets defaultProfile.extensionsResource (the global location) and are visible in all profiles — confirmed by Shared State Store being available in pin profiles. VSIX installs now explicitly pass the same profileLocation. --- .../codexSideloader/browser/codexSideloader.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts index e2576ddc25d..a0122c36a3d 100644 --- a/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts +++ b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts @@ -12,6 +12,7 @@ import { ILogService } from '../../../../platform/log/common/log.js'; import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; import { ISharedProcessService } from '../../../../platform/ipc/electron-browser/services.js'; import { IUserDataProfileService } from '../../../services/userDataProfile/common/userDataProfile.js'; +import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { ExtensionType } from '../../../../platform/extensions/common/extensions.js'; import { URI } from '../../../../base/common/uri.js'; @@ -56,15 +57,14 @@ export class CodexSideloaderContribution extends Disposable implements IWorkbenc @INotificationService private readonly notificationService: INotificationService, @ISharedProcessService private readonly sharedProcessService: ISharedProcessService, @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, ) { super(); - // Only run sideload in the default profile. CodexConductor owns - // extension management inside pin profiles (via explicit - // profileLocation); the shared-process 'extensions' install channel - // defaults to the default profile when no profileLocation is passed, - // so running the sideloader in a pin-profile window would silently - // and pointlessly change the default profile. + // Only run sideload in the default profile. All sideload installs + // target the global extension location (defaultProfile.extensionsResource), + // which is visible in all profiles, so there is no benefit to running + // again in a pin-profile window. if (!this.userDataProfileService.currentProfile.isDefault) { return; } @@ -173,6 +173,7 @@ export class CodexSideloaderContribution extends Disposable implements IWorkbenc await channel.call('install', [URI.parse(entry.vsix), { installGivenVersion: true, pinned: true, + profileLocation: this.userDataProfilesService.defaultProfile.extensionsResource, }]); this.logService.info(`${TAG} Installed "${entry.id}" from VSIX ${entry.vsix}`); } catch (err) { From 779e8ab92b3a8764c76939cc7cc201ff345b5b09 Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Tue, 14 Apr 2026 15:26:52 -0600 Subject: [PATCH 18/21] ci: add workflow_dispatch to pr-build --- .github/workflows/pr-build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 2e7d5ab65c7..766d1589267 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -8,6 +8,12 @@ on: pr_number: type: string required: true + workflow_dispatch: + inputs: + pr_number: + description: 'PR Number' + type: string + required: true jobs: prepare: From efe26102868546624d415019a1cbdb6d60eaeef7 Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Wed, 15 Apr 2026 13:30:56 -0600 Subject: [PATCH 19/21] fix: make sideload vsix global install --- .../contrib/codexSideloader/browser/codexSideloader.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts index a0122c36a3d..ce293f9faf1 100644 --- a/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts +++ b/src/stable/src/vs/workbench/contrib/codexSideloader/browser/codexSideloader.ts @@ -145,7 +145,7 @@ export class CodexSideloaderContribution extends Disposable implements IWorkbenc } try { - await this.extensionManagementService.installFromGallery(galleryExt); + await this.extensionManagementService.installFromGallery(galleryExt, { isMachineScoped: true }); this.logService.info(`${TAG} Installed "${id}" v${galleryExt.version}`); } catch (err) { this.logService.error(`${TAG} Failed to install "${id}"`, err); @@ -173,6 +173,7 @@ export class CodexSideloaderContribution extends Disposable implements IWorkbenc await channel.call('install', [URI.parse(entry.vsix), { installGivenVersion: true, pinned: true, + isMachineScoped: true, profileLocation: this.userDataProfilesService.defaultProfile.extensionsResource, }]); this.logService.info(`${TAG} Installed "${entry.id}" from VSIX ${entry.vsix}`); From 6005ce75cc3b202a297d93c64f399a1701449c85 Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Sat, 18 Apr 2026 21:08:38 -0600 Subject: [PATCH 20/21] Update pr-build workflow emojis and comments --- .github/workflows/pr-build.yml | 37 ++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 766d1589267..041f8ff191a 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -56,7 +56,7 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, comment_id: context.payload.comment.id, - content: 'rocket', + content: '+1', }); - uses: actions/checkout@v4 @@ -278,4 +278,37 @@ jobs: run: | RELEASE_URL="https://github.com/${{ github.repository }}/releases/tag/${VERSION}" gh pr comment "${{ needs.prepare.outputs.pr_number }}" \ - --body "Pre-release build ready: [$VERSION]($RELEASE_URL)" + --body "Pre-release: ${VERSION} ${RELEASE_URL}" + + - name: React with rocket on success + if: success() && github.event_name == 'issue_comment' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api "repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" \ + --method POST -f content='rocket' + + notify-failure: + needs: [prepare, build-macos, release] + if: always() && contains(needs.*.result, 'failure') + runs-on: ubuntu-latest + steps: + - name: Handle failure + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + if [ "${{ github.event_name }}" = "issue_comment" ]; then + PR_NUMBER="${{ github.event.issue.number }}" + else + PR_NUMBER="${{ inputs.pr_number }}" + fi + + if [[ -n "$PR_NUMBER" ]]; then + gh pr comment "${PR_NUMBER}" --body "Build failed: ${RUN_URL}" + fi + + if [ "${{ github.event_name }}" = "issue_comment" ]; then + gh api "repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" \ + --method POST -f content='-1' + fi From b653490476a41941b5220332a4749f681ba4db5f Mon Sep 17 00:00:00 2001 From: Jonah Braun Date: Mon, 20 Apr 2026 12:51:57 -0600 Subject: [PATCH 21/21] use stable verisons --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index 277e4e088af..fa195c4d3d6 100644 --- a/product.json +++ b/product.json @@ -600,9 +600,9 @@ } }, "codexSideloadExtensions": [ - { "id": "project-accelerate.codex-editor-extension", "vsix": "https://github.com/genesis-ai-dev/codex-editor/releases/download/0.24.0-pr856-d50039fa/codex-editor-extension-0.24.0-pr856-d50039fa.vsix", "version": "0.24.0-pr856-d50039fa" }, + "project-accelerate.codex-editor-extension", "project-accelerate.shared-state-store", "project-accelerate.vscode-edit-table", - { "id": "frontier-rnd.frontier-authentication", "vsix": "https://github.com/genesis-ai-dev/frontier-authentication/releases/download/0.4.24-pr25-365a7e6/frontier-authentication-0.4.24-pr25-365a7e6.vsix", "version": "0.4.24-pr25-365a7e6" } + "frontier-rnd.frontier-authentication" ] }