mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
Load the God's Eye View panel through the MCP server
Hosts serve panels from their own sites and may refuse other addresses; Codex refuses any address on the user's machine. The panel now never requests the app's server: it loads the app's panel build and data through panel_request, a tool only the panel may call, which requests the path from the app's server. One path in every host; only map providers load directly. npm run build:panel writes the panel build to dist/panel, served at /panel/: one app script, one stylesheet, Cesium's script, and Cesium's workers as one script Cesium runs from memory. Tools may now be marked app-only. The panel CORS rules are gone.
This commit is contained in:
1 parent
b11d54d8b8
commit
996b95eb45
20 files changed
+1265
-437
No files matched your search
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* The MCP Apps panel build. A panel loads the app through its MCP server,
|
||||
* one tool call per file (see src/tools/globePanel.js), so the app must be
|
||||
* few files: one app script, one stylesheet, Cesium's own script, and
|
||||
* Cesium's workers combined into one script that Cesium runs from memory.
|
||||
* The build is served at PANEL_BASE next to the app.
|
||||
*/
|
||||
|
||||
// The panel loads these paths (src/tools/globePanel.js); a test keeps the
|
||||
// two in step.
|
||||
export const PANEL_BASE = '/panel/';
|
||||
/** Cesium's workers as one script, relative to the panel build. */
|
||||
export const PANEL_WORKERS_PATH = 'cesium/workers.js';
|
||||
export const PANEL_OUT_DIR = 'dist/panel';
|
||||
|
||||
/** A browser Vite config changed to produce the panel build. */
|
||||
export function panelBuildConfig(config) {
|
||||
return {
|
||||
...config,
|
||||
base: PANEL_BASE,
|
||||
build: {
|
||||
...config.build,
|
||||
outDir: PANEL_OUT_DIR,
|
||||
emptyOutDir: true,
|
||||
modulePreload: false,
|
||||
cssCodeSplit: false,
|
||||
rollupOptions: {
|
||||
...config.build?.rollupOptions,
|
||||
output: { inlineDynamicImports: true },
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Source for Cesium's `CESIUM_WORKERS` script: every worker module, each
|
||||
* evaluated only when Cesium starts that worker. `workerNames` are the
|
||||
* module names in Cesium's Workers directory, without `.js`.
|
||||
*/
|
||||
export function workersEntrySource(workerNames, workersDir) {
|
||||
const entries = workerNames.map(
|
||||
(name) =>
|
||||
` ${JSON.stringify(name)}: () => import(${JSON.stringify(`${workersDir}/${name}.js`)}),`,
|
||||
);
|
||||
return `self.CesiumWorkers = {\n${entries.join('\n')}\n};\n`;
|
||||
}
|
||||
|
||||
const CONTENT_TYPES = {
|
||||
'.css': 'text/css',
|
||||
'.geojsonl': 'application/geo+json-seq',
|
||||
'.glb': 'model/gltf-binary',
|
||||
'.html': 'text/html',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.js': 'text/javascript',
|
||||
'.json': 'application/json',
|
||||
'.png': 'image/png',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.wasm': 'application/wasm',
|
||||
'.woff2': 'font/woff2',
|
||||
'.xml': 'application/xml',
|
||||
};
|
||||
|
||||
/**
|
||||
* Vite plugin serving the panel build at PANEL_BASE on the dev server, as
|
||||
* the preview server and hosted deployments serve it from the build output.
|
||||
*/
|
||||
export function panelBuildPlugin({ outDir = PANEL_OUT_DIR } = {}) {
|
||||
return {
|
||||
name: 'panel-build',
|
||||
configureServer(server) {
|
||||
server.middlewares.use(async (req, res, next) => {
|
||||
const { pathname } = new URL(req.url || '/', 'http://localhost');
|
||||
if (!pathname.startsWith(PANEL_BASE)) return next();
|
||||
const { readFile } = await import('node:fs/promises');
|
||||
const { extname, resolve, sep } = await import('node:path');
|
||||
const root = resolve(server.config.root, outDir);
|
||||
const relative = decodeURIComponent(pathname.slice(PANEL_BASE.length));
|
||||
const file = resolve(root, relative || 'index.html');
|
||||
if (file !== root && !file.startsWith(root + sep)) return next();
|
||||
try {
|
||||
const body = await readFile(file);
|
||||
res.setHeader(
|
||||
'Content-Type',
|
||||
CONTENT_TYPES[extname(file)] || 'application/octet-stream',
|
||||
);
|
||||
res.end(body);
|
||||
} catch {
|
||||
res.statusCode = 404;
|
||||
res.end(
|
||||
relative ? 'Not found' : 'No panel build; run npm run build:panel',
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
+2
-41
@@ -1,46 +1,7 @@
|
||||
import { applicationHtmlPlugin } from './application-html.js';
|
||||
import cesium from 'vite-plugin-cesium';
|
||||
import { embedFramingPlugin } from './embed-framing.js';
|
||||
|
||||
/** Origins allowed to read the dev and preview servers' responses. */
|
||||
export const PANEL_CORS_ORIGINS = Object.freeze([
|
||||
/^https?:\/\/(?:(?:[^:]+\.)?localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/,
|
||||
/^https:\/\/[a-z0-9]+\.claudemcpcontent\.com$/,
|
||||
/^(?:https|codex-sandbox):\/\/[a-z0-9-]+\.web-sandbox\.oaiusercontent\.com$/,
|
||||
]);
|
||||
|
||||
/**
|
||||
* Vite plugin answering cross-origin reads from PANEL_CORS_ORIGINS on every
|
||||
* path. It runs ahead of the API middlewares, which Vite's own `cors` option
|
||||
* would not cover.
|
||||
*/
|
||||
function panelCorsPlugin() {
|
||||
const install = (server) => {
|
||||
server.middlewares.use((req, res, next) => {
|
||||
const origin = req.headers.origin;
|
||||
if (
|
||||
!origin ||
|
||||
!PANEL_CORS_ORIGINS.some((allowed) => allowed.test(origin))
|
||||
)
|
||||
return next();
|
||||
res.setHeader('Access-Control-Allow-Origin', origin);
|
||||
res.setHeader('Vary', 'Origin');
|
||||
if (req.method !== 'OPTIONS') return next();
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, POST, OPTIONS');
|
||||
res.setHeader(
|
||||
'Access-Control-Allow-Headers',
|
||||
req.headers['access-control-request-headers'] || '',
|
||||
);
|
||||
res.statusCode = 204;
|
||||
res.end();
|
||||
});
|
||||
};
|
||||
return {
|
||||
name: 'panel-cors',
|
||||
configureServer: install,
|
||||
configurePreviewServer: install,
|
||||
};
|
||||
}
|
||||
import { panelBuildPlugin } from './panel.js';
|
||||
|
||||
/** Build browser assets with explicit inputs; never load environment or providers. */
|
||||
export function createBrowserViteConfig({
|
||||
@@ -54,11 +15,11 @@ export function createBrowserViteConfig({
|
||||
} = {}) {
|
||||
return {
|
||||
plugins: [
|
||||
panelCorsPlugin(),
|
||||
cesium(),
|
||||
applicationHtmlPlugin(),
|
||||
...plugins,
|
||||
embedFramingPlugin(),
|
||||
panelBuildPlugin(),
|
||||
],
|
||||
...(publicDir === undefined ? {} : { publicDir }),
|
||||
// A production build must not clean the dependency cache a running dev
|
||||
|
||||
Reference in new issue
Block a user