Skip to content

Commit ed3bf9d

Browse files
author
Codex Mobile Bridge
committed
Include web assets in packaged release
1 parent 7bba53f commit ed3bf9d

7 files changed

Lines changed: 74 additions & 13 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ jobs:
3838
$staging = Join-Path $distDir 'staging'
3939
New-Item -ItemType Directory -Path $staging -Force | Out-Null
4040
Copy-Item "$distDir/codex-mobile-bridge.exe" $staging
41+
Copy-Item "$distDir/web" (Join-Path $staging 'web') -Recurse
4142
Copy-Item 'scripts/install-service.ps1' $staging
4243
Copy-Item 'scripts/uninstall-service.ps1' $staging
4344
Copy-Item 'scripts/bridge-status.ps1' $staging

apps/bridge/src/http/server.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
import express from 'express';
22
import cors from 'cors';
33
import path from 'path';
4+
import fs from 'fs';
45
import { AuthGuard } from '@codex-mobile-bridge/security';
56
import { LocalStore } from '@codex-mobile-bridge/store';
67

8+
function resolveWebDist(): string {
9+
const candidates = [
10+
path.join(path.dirname(process.execPath), 'web'),
11+
path.resolve(process.cwd(), 'apps', 'web', 'dist'),
12+
path.resolve(__dirname, '../../../web/dist'),
13+
];
14+
15+
for (const candidate of candidates) {
16+
if (fs.existsSync(path.join(candidate, 'index.html'))) {
17+
return candidate;
18+
}
19+
}
20+
21+
return candidates[0];
22+
}
23+
724
export function createHttpServer(
825
authGuard: AuthGuard,
926
_store: LocalStore
@@ -13,7 +30,7 @@ export function createHttpServer(
1330
app.use(express.json());
1431

1532
// Serve Web/PWA static files
16-
const webDist = path.resolve(__dirname, '../../../web/dist');
33+
const webDist = resolveWebDist();
1734
app.use(express.static(webDist));
1835

1936
// Generate pairing code
@@ -53,4 +70,4 @@ export function createHttpServer(
5370
});
5471

5572
return app;
56-
}
73+
}

apps/web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "1.0.0",
44
"private": true,
55
"scripts": {
6-
"build": "esbuild src/app.ts --bundle --outfile=dist/app.js --format=iife --sourcemap",
6+
"build": "esbuild src/app.ts --bundle --outfile=dist/app.js --format=iife --sourcemap && node ../../scripts/copy-web-static.js",
77
"dev": "esbuild src/app.ts --bundle --outfile=dist/app.js --format=iife --sourcemap --watch"
88
},
99
"devDependencies": {

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@
2020
"test:integration": "jest --config jest.integration.config.js",
2121
"test:e2e:mock": "jest --config jest.e2e.config.js",
2222
"security:scan": "npm audit --audit-level=high --omit=dev && echo Secret scan: checking source files... && node scripts/check-secrets.js",
23-
"build": "tsc --build",
23+
"build": "tsc --build && npm run build -w apps/web",
2424
"bridge:probe": "node scripts/probe-codex.js",
2525
"rebuild-native": "node scripts/rebuild-native.js",
26-
"package:windows": "npm run rebuild-native && node scripts/bundle-deps.js && pkg apps/bridge/dist/service.js --target node20-win-x64 --output dist/codex-mobile-bridge.exe --compress GZip",
26+
"package:windows": "npm run build -w apps/web && npm run rebuild-native && node scripts/bundle-deps.js && pkg apps/bridge/dist/service.js --target node20-win-x64 --output dist/codex-mobile-bridge.exe --compress GZip && node scripts/stage-web-assets.js",
2727
"smoke:package": "node scripts/smoke-package.js"
2828
},
2929
"pkg": {

scripts/copy-web-static.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
4+
const repoRoot = path.resolve(__dirname, '..');
5+
const webSrc = path.join(repoRoot, 'apps', 'web', 'src');
6+
const webDist = path.join(repoRoot, 'apps', 'web', 'dist');
7+
const staticFiles = ['index.html', 'style.css', 'manifest.json'];
8+
9+
fs.mkdirSync(webDist, { recursive: true });
10+
11+
for (const file of staticFiles) {
12+
fs.copyFileSync(path.join(webSrc, file), path.join(webDist, file));
13+
console.log(`copied ${file}`);
14+
}

scripts/smoke-package.js

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
* Launches dist/codex-mobile-bridge.exe with isolated temp dirs and
55
* random ports, then validates:
66
* 1. HTTP GET /api/status returns 200 { status: 'ok' }
7-
* 2. POST /api/pairing/generate returns { code, deviceId }
8-
* 3. POST /api/pairing/verify with { code } returns { token }
9-
* 4. state.db exists in BRIDGE_DATA_DIR (SQLite works)
10-
* 5. service-crash.log has NO ABI / dlopen errors
7+
* 2. HTTP GET /index.html returns the Web/PWA shell
8+
* 3. POST /api/pairing/generate returns { code, deviceId }
9+
* 4. POST /api/pairing/verify with { code } returns { token }
10+
* 5. state.db exists in BRIDGE_DATA_DIR (SQLite works)
11+
* 6. service-crash.log has NO ABI / dlopen errors
1112
*
1213
* Exits 0 on pass, 1 on fail. No || true, no skipping checks.
1314
*/
@@ -159,11 +160,25 @@ async function run() {
159160
}
160161
}
161162

163+
if (pass) {
164+
// CHECK 2: /index.html
165+
try {
166+
const r = await httpRequest('GET', '/index.html');
167+
if (r.status !== 200 || !r.raw.includes('Codex Mobile Bridge') || !r.raw.includes('/app.js')) {
168+
pass = fail(`/index.html returned ${r.status}: ${r.raw.slice(0, 300)}`);
169+
} else {
170+
console.log(' CHECK PASS: /index.html -> Web/PWA shell');
171+
}
172+
} catch (err) {
173+
pass = fail(`/index.html request failed: ${err.message}`);
174+
}
175+
}
176+
162177
let pairingCode;
163178
let pairingDeviceId;
164179

165180
if (pass) {
166-
// CHECK 2: /api/pairing/generate
181+
// CHECK 3: /api/pairing/generate
167182
try {
168183
const r = await httpRequest('POST', '/api/pairing/generate', {});
169184
if (r.status !== 200 || !r.body || !r.body.code || !r.body.deviceId) {
@@ -179,7 +194,7 @@ async function run() {
179194
}
180195

181196
if (pass) {
182-
// CHECK 3: /api/pairing/verify with { code }
197+
// CHECK 4: /api/pairing/verify with { code }
183198
try {
184199
const r = await httpRequest('POST', '/api/pairing/verify', { code: pairingCode });
185200
if (r.status !== 200 || !r.body || !r.body.token) {
@@ -197,7 +212,7 @@ async function run() {
197212
// Kill the exe now that HTTP checks are done
198213
killTree();
199214

200-
// CHECK 4: state.db exists
215+
// CHECK 5: state.db exists
201216
const dbPath = path.join(dataDir, 'state.db');
202217
if (!fs.existsSync(dbPath)) {
203218
pass = fail(`state.db not found at ${dbPath} — SQLite may have crashed`);
@@ -206,7 +221,7 @@ async function run() {
206221
console.log(` CHECK PASS: state.db exists (${dbSize} bytes)`);
207222
}
208223

209-
// CHECK 5: crash log has NO fatal ABI errors
224+
// CHECK 6: crash log has NO fatal ABI errors
210225
if (fs.existsSync(crashLog)) {
211226
const content = fs.readFileSync(crashLog, 'utf-8');
212227
if (/NODE_MODULE_VERSION/.test(content)) {

scripts/stage-web-assets.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
4+
const repoRoot = path.resolve(__dirname, '..');
5+
const webDist = path.join(repoRoot, 'apps', 'web', 'dist');
6+
const runtimeWeb = path.join(repoRoot, 'dist', 'web');
7+
8+
if (!fs.existsSync(path.join(webDist, 'index.html'))) {
9+
throw new Error(`Web build output not found at ${webDist}. Run npm run build -w apps/web first.`);
10+
}
11+
12+
fs.rmSync(runtimeWeb, { recursive: true, force: true });
13+
fs.cpSync(webDist, runtimeWeb, { recursive: true });
14+
console.log(`staged web assets: ${runtimeWeb}`);

0 commit comments

Comments
 (0)