feat(dev): auto-switch ports on dev:all when defaults are busy (#9)
* 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. * Add contributing guidelines in English and Chinese - Introduced CONTRIBUTING.md and CONTRIBUTING.zh-CN.md to provide clear instructions for contributors. - Outlined contribution types, local setup instructions, and merging criteria for skills and design systems. - Enhanced README files to reference the new contributing guidelines. * Update README and documentation for deck framework directives - Clarified DECK_FRAMEWORK_DIRECTIVE description in both English and Chinese README files to specify conditions for deck kind without a skill seed. - Added detailed workflow instructions in deck-framework.ts to emphasize the importance of copying the framework before adding content. - Enhanced discovery.ts to reinforce the framework-first approach for deck projects. - Updated system.ts to ensure proper handling of deck projects with and without bound skills, preventing re-authorship of scaling and navigation logic. * Update README and documentation for deck framework directives - Clarified DECK_FRAMEWORK_DIRECTIVE description in both English and Chinese README files to specify conditions for deck kind without a skill seed. - Added detailed workflow instructions in deck-framework.ts to emphasize the importance of copying the framework before adding content. - Enhanced discovery.ts to reinforce the framework-first approach for deck projects. - Updated system.ts to ensure proper handling of deck projects with and without bound skills, preventing re-authorship of scaling and navigation logic. * Enhance README and add star promotion assets - Added a "Star us" section in both English and Chinese README files to encourage users to star the project on GitHub. - Included a new image asset for the star promotion. - Introduced a new HTML file for a dedicated star promotion page. - Updated .gitignore to exclude new cursor-related files. * feat(dev): auto-switch ports on dev:all when defaults are busy Adds a small launcher (scripts/dev-all.mjs) that probes free ports for the daemon (OD_PORT, default 7456) and Vite (VITE_PORT, default 5173) before invoking concurrently, so a stray process holding either port no longer breaks the boot. The resolved ports are exported into the child env; vite.config.ts now reads VITE_PORT to keep its dev server and /api proxy aligned with the daemon's actual port. Made-with: Cursor
This commit is contained in:
+1
-1
@@ -11,7 +11,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"daemon": "node daemon/cli.js --no-open",
|
"daemon": "node daemon/cli.js --no-open",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"dev:all": "concurrently -k -n daemon,web -c cyan,magenta \"npm:daemon\" \"npm:dev\"",
|
"dev:all": "node scripts/dev-all.mjs",
|
||||||
"build": "tsc -b && vite build",
|
"build": "tsc -b && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"typecheck": "tsc -b --noEmit",
|
"typecheck": "tsc -b --noEmit",
|
||||||
|
|||||||
Executable
+84
@@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// Launcher for `npm run dev:all`.
|
||||||
|
//
|
||||||
|
// Probes for free ports for the daemon (OD_PORT, default 7456) and the Vite
|
||||||
|
// dev server (VITE_PORT, default 5173) 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
|
||||||
|
// * vite.config.ts reads the same OD_PORT and points its /api proxy at
|
||||||
|
// the daemon's actual port
|
||||||
|
// * Vite itself binds to VITE_PORT
|
||||||
|
//
|
||||||
|
// 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 desiredVite = Number(process.env.VITE_PORT) || 5173;
|
||||||
|
|
||||||
|
const daemonPort = await findFreePort(desiredDaemon, 'daemon');
|
||||||
|
const vitePort = await findFreePort(desiredVite, 'vite');
|
||||||
|
|
||||||
|
if (daemonPort !== desiredDaemon) {
|
||||||
|
console.log(
|
||||||
|
`[dev:all] daemon port ${desiredDaemon} is busy, switching to ${daemonPort}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (vitePort !== desiredVite) {
|
||||||
|
console.log(
|
||||||
|
`[dev:all] vite port ${desiredVite} is busy, switching to ${vitePort}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
...process.env,
|
||||||
|
OD_PORT: String(daemonPort),
|
||||||
|
VITE_PORT: String(vitePort),
|
||||||
|
};
|
||||||
|
|
||||||
|
// We spawn the local `concurrently` bin via shell so Windows .cmd shims
|
||||||
|
// resolve correctly. The `npm:daemon` / `npm:dev` shorthand runs the
|
||||||
|
// matching package.json scripts, so any future tweak to those scripts is
|
||||||
|
// picked up automatically.
|
||||||
|
const child = spawn(
|
||||||
|
'concurrently',
|
||||||
|
['-k', '-n', 'daemon,web', '-c', 'cyan,magenta', 'npm:daemon', 'npm:dev'],
|
||||||
|
{ env, stdio: 'inherit', shell: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
+2
-1
@@ -2,11 +2,12 @@ import { defineConfig } from 'vite';
|
|||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react';
|
||||||
|
|
||||||
const DAEMON_PORT = Number(process.env.OD_PORT) || 7456;
|
const DAEMON_PORT = Number(process.env.OD_PORT) || 7456;
|
||||||
|
const VITE_PORT = Number(process.env.VITE_PORT) || 5173;
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
server: {
|
server: {
|
||||||
port: 5173,
|
port: VITE_PORT,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: `http://127.0.0.1:${DAEMON_PORT}`,
|
target: `http://127.0.0.1:${DAEMON_PORT}`,
|
||||||
|
|||||||
Reference in New Issue
Block a user