lobster-farmer-cli 0.1.1 → 0.1.2
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/bin/lobster-farmer.cjs +109 -4
- package/package.json +7 -1
package/bin/lobster-farmer.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { spawn, spawnSync } = require("node:child_process");
|
|
4
4
|
const { existsSync, rmSync, mkdirSync, writeFileSync, readFileSync, openSync } = require("node:fs");
|
|
5
|
-
const { homedir } = require("node:os");
|
|
5
|
+
const { homedir, networkInterfaces } = require("node:os");
|
|
6
6
|
const { resolve } = require("node:path");
|
|
7
7
|
|
|
8
8
|
const entry = resolve(__dirname, "../dist/index.js");
|
|
@@ -12,6 +12,7 @@ const dataDir = resolve(homedir(), ".lobster-farmer");
|
|
|
12
12
|
const dbBase = resolve(dataDir, "data.sqlite");
|
|
13
13
|
const pidFile = resolve(dataDir, "lobster-farmer.pid");
|
|
14
14
|
const logFile = resolve(dataDir, "lobster-farmer.log");
|
|
15
|
+
const portFile = resolve(dataDir, "lobster-farmer.port");
|
|
15
16
|
const defaultPort = "18990";
|
|
16
17
|
|
|
17
18
|
function printHelp() {
|
|
@@ -157,6 +158,7 @@ function resetData() {
|
|
|
157
158
|
removeIfExists(`${dbBase}-wal`);
|
|
158
159
|
removeIfExists(pidFile);
|
|
159
160
|
removeIfExists(logFile);
|
|
161
|
+
removeIfExists(portFile);
|
|
160
162
|
console.log("SQLite data reset complete.");
|
|
161
163
|
}
|
|
162
164
|
|
|
@@ -206,20 +208,115 @@ function getRunningPid() {
|
|
|
206
208
|
}
|
|
207
209
|
|
|
208
210
|
removeIfExists(pidFile);
|
|
211
|
+
removeIfExists(portFile);
|
|
209
212
|
return null;
|
|
210
213
|
}
|
|
211
214
|
|
|
215
|
+
function readActivePort() {
|
|
216
|
+
if (!existsSync(portFile)) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const value = readFileSync(portFile, "utf8").trim();
|
|
221
|
+
const numeric = Number(value);
|
|
222
|
+
if (!Number.isInteger(numeric) || numeric <= 0 || numeric > 65535) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
return String(numeric);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function writeActivePort(port) {
|
|
229
|
+
writeFileSync(portFile, `${port}\n`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function accessUrl(port) {
|
|
233
|
+
return `http://localhost:${port}`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function isPrivateLanIpv4(address) {
|
|
237
|
+
const parts = address.split(".").map((part) => Number(part));
|
|
238
|
+
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const [a, b] = parts;
|
|
243
|
+
if (a === 10) {
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
if (a === 172 && b >= 16 && b <= 31) {
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
if (a === 192 && b === 168) {
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
if (a === 100 && b >= 64 && b <= 127) {
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function lanAccessUrls(port) {
|
|
260
|
+
const nets = networkInterfaces();
|
|
261
|
+
const urls = [];
|
|
262
|
+
const seen = new Set();
|
|
263
|
+
|
|
264
|
+
for (const interfaces of Object.values(nets)) {
|
|
265
|
+
if (!Array.isArray(interfaces)) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
for (const iface of interfaces) {
|
|
270
|
+
if (!iface || iface.internal) {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const family = typeof iface.family === "string" ? iface.family : iface.family === 4 ? "IPv4" : "";
|
|
275
|
+
if (family !== "IPv4") {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (!iface.address || !isPrivateLanIpv4(iface.address)) {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const url = `http://${iface.address}:${port}`;
|
|
284
|
+
if (seen.has(url)) {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
seen.add(url);
|
|
288
|
+
urls.push(url);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return urls;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function printAccessUrls(port) {
|
|
296
|
+
console.log(`url: ${accessUrl(port)}`);
|
|
297
|
+
|
|
298
|
+
const lanUrls = lanAccessUrls(port);
|
|
299
|
+
if (lanUrls.length > 0) {
|
|
300
|
+
console.log("lan:");
|
|
301
|
+
for (const url of lanUrls) {
|
|
302
|
+
console.log(` - ${url}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
212
307
|
function startServer(port, foreground) {
|
|
213
308
|
ensureBuild();
|
|
214
309
|
|
|
215
310
|
mkdirSync(dataDir, { recursive: true });
|
|
311
|
+
const resolvedPort = resolvePort(port);
|
|
216
312
|
|
|
217
313
|
const env = { ...process.env };
|
|
218
|
-
|
|
219
|
-
env.PORT = port;
|
|
220
|
-
}
|
|
314
|
+
env.PORT = resolvedPort;
|
|
221
315
|
|
|
222
316
|
if (foreground) {
|
|
317
|
+
writeActivePort(resolvedPort);
|
|
318
|
+
printAccessUrls(resolvedPort);
|
|
319
|
+
|
|
223
320
|
const child = spawn(process.execPath, [entry], {
|
|
224
321
|
stdio: "inherit",
|
|
225
322
|
env
|
|
@@ -231,6 +328,7 @@ function startServer(port, foreground) {
|
|
|
231
328
|
});
|
|
232
329
|
|
|
233
330
|
child.on("exit", (code, signal) => {
|
|
331
|
+
removeIfExists(portFile);
|
|
234
332
|
if (signal) {
|
|
235
333
|
process.kill(process.pid, signal);
|
|
236
334
|
return;
|
|
@@ -243,7 +341,9 @@ function startServer(port, foreground) {
|
|
|
243
341
|
|
|
244
342
|
const existingPid = getRunningPid();
|
|
245
343
|
if (existingPid) {
|
|
344
|
+
const runningPort = readActivePort() || resolvedPort;
|
|
246
345
|
console.log(`Lobster Farmer already running (pid: ${existingPid})`);
|
|
346
|
+
printAccessUrls(runningPort);
|
|
247
347
|
return;
|
|
248
348
|
}
|
|
249
349
|
|
|
@@ -257,8 +357,10 @@ function startServer(port, foreground) {
|
|
|
257
357
|
child.unref();
|
|
258
358
|
|
|
259
359
|
writeFileSync(pidFile, String(child.pid));
|
|
360
|
+
writeActivePort(resolvedPort);
|
|
260
361
|
console.log(`Lobster Farmer started in background.`);
|
|
261
362
|
console.log(`pid: ${child.pid}`);
|
|
363
|
+
printAccessUrls(resolvedPort);
|
|
262
364
|
console.log(`log: ${logFile}`);
|
|
263
365
|
}
|
|
264
366
|
|
|
@@ -277,6 +379,7 @@ function stopServer() {
|
|
|
277
379
|
}
|
|
278
380
|
|
|
279
381
|
removeIfExists(pidFile);
|
|
382
|
+
removeIfExists(portFile);
|
|
280
383
|
console.log(`Lobster Farmer stopped (pid: ${pid}).`);
|
|
281
384
|
}
|
|
282
385
|
|
|
@@ -288,6 +391,8 @@ function showStatus() {
|
|
|
288
391
|
}
|
|
289
392
|
|
|
290
393
|
console.log(`Lobster Farmer status: running (pid: ${pid})`);
|
|
394
|
+
const runningPort = readActivePort() || defaultPort;
|
|
395
|
+
printAccessUrls(runningPort);
|
|
291
396
|
console.log(`log: ${logFile}`);
|
|
292
397
|
}
|
|
293
398
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lobster-farmer-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Lobster Farmer CLI for OpenClaw auto-feeding and lobster growth simulation",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,6 +22,12 @@
|
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
24
|
"packageManager": "pnpm@10.14.0",
|
|
25
|
+
"pnpm": {
|
|
26
|
+
"onlyBuiltDependencies": [
|
|
27
|
+
"better-sqlite3",
|
|
28
|
+
"esbuild"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
25
31
|
"scripts": {
|
|
26
32
|
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
27
33
|
"dev": "tsx watch server/index.ts",
|