Compare commits

...

1 Commits

Author SHA1 Message Date
Drew Ritter
43f9791850 fix: read Codex plugin version from manifest (PRI-2240) 2026-06-16 12:13:43 -07:00
2 changed files with 59 additions and 16 deletions

View File

@@ -206,14 +206,22 @@ const helperInjection = '<script>\n' + helperScript + '\n</script>';
// ========== Helper Functions ========== // ========== Helper Functions ==========
function readSuperpowersVersion() { function readSuperpowersVersion() {
try { const root = path.join(__dirname, '../../..');
const packageJson = JSON.parse( const manifests = [
fs.readFileSync(path.join(__dirname, '../../..', 'package.json'), 'utf-8') path.join(root, 'package.json'),
); path.join(root, '.codex-plugin/plugin.json')
return String(packageJson.version || 'unknown'); ];
} catch (e) {
return 'unknown'; for (const manifest of manifests) {
try {
const data = JSON.parse(fs.readFileSync(manifest, 'utf-8'));
if (data.version) return String(data.version);
} catch (e) {
// Packaged Codex plugins omit package.json; try the next manifest.
}
} }
return 'unknown';
} }
function isTruthyEnv(value) { function isTruthyEnv(value) {

View File

@@ -26,9 +26,9 @@ function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms)); return new Promise(resolve => setTimeout(resolve, ms));
} }
function startServer({ port, dir, env = {} }) { function startServer({ port, dir, env = {}, serverPath = SERVER_PATH }) {
cleanup(dir); cleanup(dir);
return spawn('node', [SERVER_PATH], { return spawn('node', [serverPath], {
env: { env: {
...process.env, ...process.env,
BRAINSTORM_PORT: String(port), BRAINSTORM_PORT: String(port),
@@ -74,6 +74,21 @@ function writeFragment(dir) {
fs.writeFileSync(path.join(contentDir, 'screen.html'), '<h2>Pick a layout</h2>'); fs.writeFileSync(path.join(contentDir, 'screen.html'), '<h2>Pick a layout</h2>');
} }
function createPackagedServerFixture(version) {
const root = fs.mkdtempSync(path.join('/tmp', 'superpowers-packaged-server-'));
const scriptDir = path.join(root, 'skills/brainstorming/scripts');
fs.cpSync(path.join(REPO_ROOT, 'skills/brainstorming/scripts'), scriptDir, { recursive: true });
fs.mkdirSync(path.join(root, '.codex-plugin'), { recursive: true });
fs.writeFileSync(
path.join(root, '.codex-plugin/plugin.json'),
JSON.stringify({ name: 'superpowers', version }, null, 2)
);
return {
root,
serverPath: path.join(scriptDir, 'server.cjs')
};
}
async function withServer(options, fn) { async function withServer(options, fn) {
const server = startServer(options); const server = startServer(options);
try { try {
@@ -104,13 +119,13 @@ async function test(name, fn) {
} }
} }
function assertBrandedWithLogo(html) { function assertBrandedWithLogo(html, version = PACKAGE_VERSION) {
assert( assert(
html.includes(`Superpowers v${PACKAGE_VERSION}`), html.includes(`Superpowers v${version}`),
'branding text should include dynamic package version' 'branding text should include dynamic package version'
); );
assert( assert(
!html.includes(`Superpowers v${PACKAGE_VERSION} by`), !html.includes(`Superpowers v${version} by`),
'branding text should not include "by" when the logo is visible' 'branding text should not include "by" when the logo is visible'
); );
assert( assert(
@@ -139,15 +154,15 @@ function assertBrandedWithLogo(html) {
); );
} }
function assertBrandedFallbackText(html) { function assertBrandedFallbackText(html, version = PACKAGE_VERSION) {
assert( assert(
html.includes(`Prime Radiant Superpowers v${PACKAGE_VERSION}`), html.includes(`Prime Radiant Superpowers v${version}`),
'disabled telemetry should keep plain text Prime Radiant/Superpowers branding' 'disabled telemetry should keep plain text Prime Radiant/Superpowers branding'
); );
} }
function assertTelemetryImage(html) { function assertTelemetryImage(html, version = PACKAGE_VERSION) {
const expectedUrl = `${ASSET_URL}?v=${encodeURIComponent(PACKAGE_VERSION)}`; const expectedUrl = `${ASSET_URL}?v=${encodeURIComponent(version)}`;
assert(html.includes(`src="${expectedUrl}"`), 'remote image should use the dedicated main-domain asset with only v='); assert(html.includes(`src="${expectedUrl}"`), 'remote image should use the dedicated main-domain asset with only v=');
assert(!html.includes('event='), 'remote image URL must not include event='); assert(!html.includes('event='), 'remote image URL must not include event=');
assert(!html.includes('surface='), 'remote image URL must not include surface='); assert(!html.includes('surface='), 'remote image URL must not include surface=');
@@ -255,6 +270,26 @@ async function main() {
}); });
}); });
await test('packaged Codex plugin reads version from .codex-plugin manifest', async () => {
const port = 3457;
const dir = '/tmp/brainstorm-branding-packaged-codex';
const packagedVersion = '7.8.9';
const fixture = createPackagedServerFixture(packagedVersion);
try {
await withServer({ port, dir, serverPath: fixture.serverPath }, async () => {
writeFragment(dir);
await sleep(300);
const html = await fetchHtml(port);
assertBrandedWithLogo(html, packagedVersion);
assertTelemetryImage(html, packagedVersion);
assert(!html.includes('Superpowers vunknown'), 'packaged plugin should not fall back to unknown version');
});
} finally {
cleanup(fixture.root);
}
});
await test('SUPERPOWERS_DISABLE_TELEMETRY=true omits remote image but keeps local branding', async () => { await test('SUPERPOWERS_DISABLE_TELEMETRY=true omits remote image but keeps local branding', async () => {
const port = 3453; const port = 3453;
const dir = '/tmp/brainstorm-branding-disabled'; const dir = '/tmp/brainstorm-branding-disabled';