@neat.is/core 0.2.9 → 0.2.10

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/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  routeSpanToProject,
3
3
  startDaemon
4
- } from "./chunk-BVF7MVR5.js";
4
+ } from "./chunk-5BQWBQJR.js";
5
5
  import {
6
6
  buildApi,
7
7
  computeGraphDiff,
8
8
  loadSnapshotForDiff
9
- } from "./chunk-5KX7EI4F.js";
9
+ } from "./chunk-W7ZYJZC7.js";
10
10
  import {
11
11
  ProjectNameCollisionError,
12
12
  addProject,
@@ -39,13 +39,14 @@ import {
39
39
  thresholdForEdgeType,
40
40
  touchLastSeen,
41
41
  writeAtomically
42
- } from "./chunk-IRPH6KL4.js";
42
+ } from "./chunk-JMLZOKCS.js";
43
43
  import {
44
44
  buildOtelReceiver,
45
45
  logSpanHandler,
46
46
  parseOtlpRequest,
47
47
  startOtelGrpcReceiver
48
- } from "./chunk-I5IMCXRO.js";
48
+ } from "./chunk-QYUB3FQL.js";
49
+ import "./chunk-DGUM43GV.js";
49
50
  export {
50
51
  ProjectNameCollisionError,
51
52
  addProject,
package/dist/neatd.cjs CHANGED
@@ -25,7 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/neatd.ts
27
27
  var import_node_fs19 = require("fs");
28
- var import_node_path32 = __toESM(require("path"), 1);
28
+ var import_node_path33 = __toESM(require("path"), 1);
29
29
 
30
30
  // src/daemon.ts
31
31
  var import_node_fs18 = require("fs");
@@ -3116,17 +3116,98 @@ async function startDaemon(opts = {}) {
3116
3116
  return { slots, reload, stop, pidPath };
3117
3117
  }
3118
3118
 
3119
+ // src/web-spawn.ts
3120
+ var import_node_child_process = require("child_process");
3121
+ var import_node_net = __toESM(require("net"), 1);
3122
+ var import_node_path32 = __toESM(require("path"), 1);
3123
+ var DEFAULT_WEB_PORT = 6328;
3124
+ async function assertPortFree(port) {
3125
+ await new Promise((resolve, reject) => {
3126
+ const tester = import_node_net.default.createServer();
3127
+ tester.once("error", (err) => {
3128
+ if (err.code === "EADDRINUSE") {
3129
+ reject(
3130
+ new Error(
3131
+ `neatd: web UI port ${port} in use; set NEAT_WEB_PORT to override or stop the conflicting process`
3132
+ )
3133
+ );
3134
+ } else {
3135
+ reject(err);
3136
+ }
3137
+ });
3138
+ tester.once("listening", () => {
3139
+ tester.close(() => resolve());
3140
+ });
3141
+ tester.listen(port, "127.0.0.1");
3142
+ });
3143
+ }
3144
+ function resolveWebPackageDir() {
3145
+ const req = typeof require !== "undefined" ? require : (
3146
+ // ESM fallback — daemon CJS bundle has `require`, but typecheck wants this
3147
+ eval("require")
3148
+ );
3149
+ const pkgJsonPath = req.resolve("@neat.is/web/package.json");
3150
+ return import_node_path32.default.dirname(pkgJsonPath);
3151
+ }
3152
+ async function spawnWebUI(restPort) {
3153
+ const portRaw = process.env.NEAT_WEB_PORT;
3154
+ const port = portRaw && portRaw.length > 0 ? Number.parseInt(portRaw, 10) : DEFAULT_WEB_PORT;
3155
+ if (!Number.isFinite(port) || port <= 0 || port > 65535) {
3156
+ throw new Error(`neatd: invalid NEAT_WEB_PORT="${portRaw}"`);
3157
+ }
3158
+ await assertPortFree(port);
3159
+ const cwd = resolveWebPackageDir();
3160
+ const env = {
3161
+ ...process.env,
3162
+ PORT: String(port),
3163
+ NEAT_API_URL: process.env.NEAT_API_URL ?? `http://localhost:${restPort}`
3164
+ };
3165
+ const child = (0, import_node_child_process.spawn)("npm", ["exec", "--", "next", "start", "-p", String(port)], {
3166
+ cwd,
3167
+ env,
3168
+ stdio: ["ignore", "inherit", "inherit"],
3169
+ detached: false
3170
+ });
3171
+ child.on("error", (err) => {
3172
+ console.error(`neatd: web UI spawn error \u2014 ${err.message}`);
3173
+ });
3174
+ console.log(`neatd: web UI listening on http://localhost:${port}`);
3175
+ let stopped = false;
3176
+ async function stop() {
3177
+ if (stopped || !child.pid) return;
3178
+ stopped = true;
3179
+ try {
3180
+ child.kill("SIGTERM");
3181
+ } catch {
3182
+ }
3183
+ await new Promise((resolve) => {
3184
+ const t = setTimeout(() => {
3185
+ try {
3186
+ child.kill("SIGKILL");
3187
+ } catch {
3188
+ }
3189
+ resolve();
3190
+ }, 3e3);
3191
+ child.once("exit", () => {
3192
+ clearTimeout(t);
3193
+ resolve();
3194
+ });
3195
+ });
3196
+ }
3197
+ return { child, port, stop };
3198
+ }
3199
+
3119
3200
  // src/neatd.ts
3120
3201
  function neatHome2() {
3121
3202
  if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
3122
- return import_node_path32.default.resolve(process.env.NEAT_HOME);
3203
+ return import_node_path33.default.resolve(process.env.NEAT_HOME);
3123
3204
  }
3124
3205
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
3125
- return import_node_path32.default.join(home, ".neat");
3206
+ return import_node_path33.default.join(home, ".neat");
3126
3207
  }
3127
3208
  async function readPid() {
3128
3209
  try {
3129
- const raw = await import_node_fs19.promises.readFile(import_node_path32.default.join(neatHome2(), "neatd.pid"), "utf8");
3210
+ const raw = await import_node_fs19.promises.readFile(import_node_path33.default.join(neatHome2(), "neatd.pid"), "utf8");
3130
3211
  const n = Number.parseInt(raw.trim(), 10);
3131
3212
  return Number.isFinite(n) ? n : null;
3132
3213
  } catch {
@@ -3136,17 +3217,35 @@ async function readPid() {
3136
3217
  function usage() {
3137
3218
  console.log("usage: neatd <start|stop|reload|status> [--foreground]");
3138
3219
  }
3220
+ function restPortFromEnv() {
3221
+ const raw = process.env.PORT;
3222
+ return raw && raw.length > 0 ? Number.parseInt(raw, 10) : 8080;
3223
+ }
3139
3224
  async function cmdStart() {
3140
3225
  const handle = await startDaemon();
3141
3226
  console.log(`neatd: started, PID ${process.pid}, ${handle.slots.size} project(s)`);
3142
3227
  console.log(`neatd: registry at ${registryPath()}`);
3228
+ const skipWeb = process.env.NEAT_WEB_DISABLED === "1";
3229
+ let web = null;
3230
+ if (!skipWeb) {
3231
+ try {
3232
+ web = await spawnWebUI(restPortFromEnv());
3233
+ } catch (err) {
3234
+ console.error(err.message);
3235
+ await handle.stop().catch(() => {
3236
+ });
3237
+ process.exit(3);
3238
+ }
3239
+ } else {
3240
+ console.log("neatd: web UI disabled (NEAT_WEB_DISABLED=1)");
3241
+ }
3143
3242
  console.log("neatd: SIGHUP reloads, SIGTERM/SIGINT stops");
3144
3243
  let stopping = false;
3145
3244
  const shutdown = (signal) => {
3146
3245
  if (stopping) return;
3147
3246
  stopping = true;
3148
3247
  console.log(`neatd: ${signal} received, stopping\u2026`);
3149
- void handle.stop().catch((err) => console.error(`neatd: shutdown error \u2014 ${err.message}`)).finally(() => process.exit(0));
3248
+ void Promise.allSettled([handle.stop(), web ? web.stop() : Promise.resolve()]).catch((err) => console.error(`neatd: shutdown error \u2014 ${err.message}`)).finally(() => process.exit(0));
3150
3249
  };
3151
3250
  process.on("SIGTERM", shutdown);
3152
3251
  process.on("SIGINT", shutdown);
@@ -3185,6 +3284,8 @@ async function cmdStatus() {
3185
3284
  const pid = await readPid();
3186
3285
  console.log(`pid: ${pid ?? "(not running)"}`);
3187
3286
  console.log(`registry: ${registryPath()}`);
3287
+ const webPort = process.env.NEAT_WEB_PORT ? Number.parseInt(process.env.NEAT_WEB_PORT, 10) : DEFAULT_WEB_PORT;
3288
+ console.log(`web ui: http://localhost:${webPort}`);
3188
3289
  const projects = await listProjects().catch(() => []);
3189
3290
  if (projects.length === 0) {
3190
3291
  console.log("projects: (none)");