mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
* Refactor project name from "Open Claude Design" to "Open Design" - Updated project name in package.json, package-lock.json, and README files. - Changed CLI commands and references from "ocd" to "od". - Adjusted file structure references in documentation and code to reflect new naming conventions. - Enhanced .gitignore to include new runtime data files. - Updated metadata in LICENSE file to match new project name. * chore: migrate frontend toolchain from Vite to Next.js 16 App Router Replace the Vite SPA scaffold with Next.js 16 App Router while keeping the existing daemon as the API/SSE/sqlite backend. The whole client tree now mounts under a single optional catch-all route (app/[[...slug]]) loaded with ssr:false; static export emits one shell HTML the daemon serves as the SPA fallback for deep links. Dev uses next.config rewrites to proxy /api, /artifacts, /frames to the daemon, matching the previous Vite setup. Made-with: Cursor * fix: address Next migration review feedback * fix: serve static export in preview script --------- Co-authored-by: mrcfps <mrc@powerformer.com>
90 lines
2.9 KiB
JavaScript
Executable File
90 lines
2.9 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
// Launcher for `npm run dev:all`.
|
|
//
|
|
// Probes for free ports for the daemon (OD_PORT, default 7456) and the
|
|
// Next.js dev server (NEXT_PORT, default 3000) before spawning
|
|
// `concurrently`, so a stray process holding either port doesn't kill the
|
|
// whole boot. The resolved ports are exported into the child env, which
|
|
// means:
|
|
// * the daemon's cli.js sees the new OD_PORT and binds to it
|
|
// * next.config.ts reads the same OD_PORT and proxies /api, /artifacts,
|
|
// /frames to the daemon's actual port
|
|
// * Next.js binds to NEXT_PORT (we pass `next dev -p $NEXT_PORT` so the
|
|
// `dev` script can stay parameter-free for the common single-process
|
|
// case where the user runs just `pnpm dev`)
|
|
//
|
|
// If a port is busy we walk forward up to PORT_SEARCH_RANGE steps and log
|
|
// the switch so the user notices.
|
|
|
|
import { spawn } from 'node:child_process';
|
|
import net from 'node:net';
|
|
|
|
const HOST = '127.0.0.1';
|
|
const PORT_SEARCH_RANGE = 50;
|
|
|
|
function isPortFree(port, host = HOST) {
|
|
return new Promise((resolve) => {
|
|
const server = net.createServer();
|
|
server.unref();
|
|
server.once('error', () => resolve(false));
|
|
server.listen({ port, host, exclusive: true }, () => {
|
|
server.close(() => resolve(true));
|
|
});
|
|
});
|
|
}
|
|
|
|
async function findFreePort(start, label) {
|
|
for (let port = start; port < start + PORT_SEARCH_RANGE; port++) {
|
|
if (await isPortFree(port)) return port;
|
|
}
|
|
throw new Error(
|
|
`[dev:all] could not find a free ${label} port near ${start} (tried ${PORT_SEARCH_RANGE})`,
|
|
);
|
|
}
|
|
|
|
const desiredDaemon = Number(process.env.OD_PORT) || 7456;
|
|
const desiredNext = Number(process.env.NEXT_PORT) || 3000;
|
|
|
|
const daemonPort = await findFreePort(desiredDaemon, 'daemon');
|
|
const nextPort = await findFreePort(desiredNext, 'next');
|
|
|
|
if (daemonPort !== desiredDaemon) {
|
|
console.log(
|
|
`[dev:all] daemon port ${desiredDaemon} is busy, switching to ${daemonPort}`,
|
|
);
|
|
}
|
|
if (nextPort !== desiredNext) {
|
|
console.log(
|
|
`[dev:all] next port ${desiredNext} is busy, switching to ${nextPort}`,
|
|
);
|
|
}
|
|
|
|
const env = {
|
|
...process.env,
|
|
OD_PORT: String(daemonPort),
|
|
NEXT_PORT: String(nextPort),
|
|
PORT: String(nextPort),
|
|
};
|
|
|
|
// `npm:daemon` is the shorthand for the daemon script, and `next dev -p
|
|
// <port>` is invoked directly so we can pass the resolved port without
|
|
// round-tripping through npm scripts. Keep the port numeric before it reaches
|
|
// the command string, and avoid shell interpretation on POSIX; Windows needs
|
|
// shell mode so the local `.cmd` shim can resolve.
|
|
const child = spawn(
|
|
'concurrently',
|
|
['-k', '-n', 'daemon,web', '-c', 'cyan,magenta', 'npm:daemon', `next dev -p ${nextPort}`],
|
|
{ env, stdio: 'inherit', shell: process.platform === 'win32' },
|
|
);
|
|
|
|
child.on('exit', (code, signal) => {
|
|
if (signal) process.kill(process.pid, signal);
|
|
else process.exit(code ?? 0);
|
|
});
|
|
|
|
for (const sig of ['SIGINT', 'SIGTERM']) {
|
|
process.on(sig, () => {
|
|
if (!child.killed) child.kill(sig);
|
|
});
|
|
}
|