castle-web-cli 0.4.9 → 0.4.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/init.js +0 -1
- package/dist/serve.js +52 -3
- package/kits/basic-2d/package.json +0 -1
- package/package.json +1 -1
package/dist/init.js
CHANGED
|
@@ -70,7 +70,6 @@ function makePackageJson(projectDir) {
|
|
|
70
70
|
private: true,
|
|
71
71
|
type: 'module',
|
|
72
72
|
scripts: {
|
|
73
|
-
serve: `node ${cliEntry} serve . --open`,
|
|
74
73
|
restart: `node ${cliEntry} restart .`,
|
|
75
74
|
screenshot: `node ${cliEntry} screenshot .`,
|
|
76
75
|
'save-deck': `node ${cliEntry} save-deck .`,
|
package/dist/serve.js
CHANGED
|
@@ -74,6 +74,38 @@ function readDeckContext(projectDir) {
|
|
|
74
74
|
}
|
|
75
75
|
const DEFAULT_PORT = 5757;
|
|
76
76
|
const SCREENSHOT_REQUEST_TTL_MS = 15_000;
|
|
77
|
+
function readServeJson(projectDir) {
|
|
78
|
+
try {
|
|
79
|
+
const raw = fs.readFileSync(path.join(projectDir, '.castle', 'serve.json'), 'utf8');
|
|
80
|
+
const info = JSON.parse(raw);
|
|
81
|
+
if (typeof info.port !== 'number' || typeof info.wsPort !== 'number' || typeof info.pid !== 'number')
|
|
82
|
+
return null;
|
|
83
|
+
return { port: info.port, wsPort: info.wsPort, pid: info.pid };
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function isPidAlive(pid) {
|
|
90
|
+
try {
|
|
91
|
+
process.kill(pid, 0);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Return the live serve already owning this deck, if any. Lets repeated
|
|
99
|
+
// `castle-web serve`/`npm run serve` calls become idempotent instead of
|
|
100
|
+
// piling up independent vite instances on adjacent ports.
|
|
101
|
+
function existingServe(projectDir) {
|
|
102
|
+
const info = readServeJson(projectDir);
|
|
103
|
+
if (!info)
|
|
104
|
+
return null;
|
|
105
|
+
if (!isPidAlive(info.pid))
|
|
106
|
+
return null;
|
|
107
|
+
return info;
|
|
108
|
+
}
|
|
77
109
|
export async function serve(dir, options = {}) {
|
|
78
110
|
const projectDir = path.resolve(dir);
|
|
79
111
|
if (!fs.existsSync(projectDir)) {
|
|
@@ -84,6 +116,18 @@ export async function serve(dir, options = {}) {
|
|
|
84
116
|
console.error(`No index.html found in ${projectDir}`);
|
|
85
117
|
process.exit(1);
|
|
86
118
|
}
|
|
119
|
+
// If a live serve is already running for this deck, reuse it. Skip the
|
|
120
|
+
// dedup when --port / --host are passed (caller wants those specifics, so
|
|
121
|
+
// start a fresh serve even if another is up).
|
|
122
|
+
if (!options.port && !options.host) {
|
|
123
|
+
const existing = existingServe(projectDir);
|
|
124
|
+
if (existing) {
|
|
125
|
+
console.log(`Serve already running for ${projectDir}`);
|
|
126
|
+
console.log(`URL: http://localhost:${existing.port}`);
|
|
127
|
+
console.log(`PID: ${existing.pid}`);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
87
131
|
// --detach is kept for back-compat. Agent evals (and any harness running
|
|
88
132
|
// castle-web in a context that can't manage a foreground process) should
|
|
89
133
|
// prefer the CASTLE_WEB_CLI_DETACH=1 env var instead.
|
|
@@ -210,12 +254,17 @@ async function serveDetached(projectDir, options) {
|
|
|
210
254
|
args.push('--host', options.host);
|
|
211
255
|
if (options.open)
|
|
212
256
|
args.push('--open');
|
|
213
|
-
//
|
|
257
|
+
// CASTLE_WEB_CLI_DETACH=1 → spawn as a new session leader (fully
|
|
258
|
+
// independent; survives caller). This is the eval-harness mode where the
|
|
259
|
+
// harness manages cleanup itself.
|
|
260
|
+
// Default → child stays in the caller's process group / session, so when
|
|
214
261
|
// the agent / shell that invoked `castle-web init` exits, SIGHUP reaches
|
|
215
|
-
// this serve and it dies with the session.
|
|
216
|
-
// the calling node process exit
|
|
262
|
+
// this serve and it dies with the session.
|
|
263
|
+
// `.unref()` is what lets the calling node process exit either way.
|
|
264
|
+
const fullDetach = process.env.CASTLE_WEB_CLI_DETACH === '1';
|
|
217
265
|
const child = spawn(process.execPath, [entry, ...args], {
|
|
218
266
|
cwd: projectDir,
|
|
267
|
+
detached: fullDetach,
|
|
219
268
|
stdio: ['ignore', logFd, logFd],
|
|
220
269
|
env: process.env,
|
|
221
270
|
});
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"serve": "node ../../cli/dist/index.js serve . --open",
|
|
7
6
|
"restart": "node ../../cli/dist/index.js restart .",
|
|
8
7
|
"screenshot": "node ../../cli/dist/index.js screenshot .",
|
|
9
8
|
"check": "eslint . && jscpd && node --input-type=module -e \"const { bundleProject } = await import('../../cli/dist/bundle.js'); await bundleProject('.');\""
|