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": {
|
||||
"daemon": "node daemon/cli.js --no-open",
|
||||
"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",
|
||||
"preview": "vite preview",
|
||||
"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';
|
||||
|
||||
const DAEMON_PORT = Number(process.env.OD_PORT) || 7456;
|
||||
const VITE_PORT = Number(process.env.VITE_PORT) || 5173;
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 5173,
|
||||
port: VITE_PORT,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: `http://127.0.0.1:${DAEMON_PORT}`,
|
||||
|
||||
Reference in New Issue
Block a user