@neat.is/core 0.3.5 → 0.3.6

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/neatd.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startDaemon
4
- } from "./chunk-KCEZSFU2.js";
4
+ } from "./chunk-YHQYHFI3.js";
5
5
  import {
6
6
  listProjects,
7
7
  registryPath
8
- } from "./chunk-BUB3ASD5.js";
8
+ } from "./chunk-ZU2RQRCN.js";
9
9
  import {
10
10
  __require
11
11
  } from "./chunk-4ASCXBZF.js";
@@ -13,6 +13,7 @@ import {
13
13
  // src/neatd.ts
14
14
  import { promises as fs } from "fs";
15
15
  import path2 from "path";
16
+ import { createRequire } from "module";
16
17
 
17
18
  // src/web-spawn.ts
18
19
  import { spawn } from "child_process";
@@ -107,7 +108,82 @@ async function spawnWebUI(restPort) {
107
108
  return { child, port, stop };
108
109
  }
109
110
 
111
+ // src/version-skew.ts
112
+ import { setTimeout as delay } from "timers/promises";
113
+ var NPM_REGISTRY_URL = "https://registry.npmjs.org/neat.is/latest";
114
+ var DEFAULT_TIMEOUT_MS = 2e3;
115
+ async function fetchRegistryVersion(url, timeoutMs, fetchImpl) {
116
+ const controller = new AbortController();
117
+ let timedOut = false;
118
+ const timer = delay(timeoutMs).then(() => {
119
+ timedOut = true;
120
+ controller.abort();
121
+ });
122
+ try {
123
+ const res = await Promise.race([
124
+ fetchImpl(url, { signal: controller.signal, headers: { accept: "application/json" } }),
125
+ timer.then(() => null)
126
+ ]);
127
+ if (timedOut || !res) return null;
128
+ if (!res.ok) return null;
129
+ const body = await res.json();
130
+ if (typeof body.version !== "string" || body.version.length === 0) return null;
131
+ return body.version;
132
+ } catch {
133
+ return null;
134
+ } finally {
135
+ if (!timedOut) controller.abort();
136
+ }
137
+ }
138
+ function isLocalBehind(local, remote) {
139
+ if (local === remote) return false;
140
+ const parse = (v) => {
141
+ const core = v.split(/[-+]/)[0] ?? v;
142
+ return core.split(".").map((s) => {
143
+ const n = Number.parseInt(s, 10);
144
+ return Number.isFinite(n) ? n : 0;
145
+ });
146
+ };
147
+ const a = parse(local);
148
+ const b = parse(remote);
149
+ const len = Math.max(a.length, b.length);
150
+ for (let i = 0; i < len; i++) {
151
+ const av = a[i] ?? 0;
152
+ const bv = b[i] ?? 0;
153
+ if (av < bv) return true;
154
+ if (av > bv) return false;
155
+ }
156
+ return false;
157
+ }
158
+ async function checkVersionSkew(opts) {
159
+ const url = opts.registryUrl ?? NPM_REGISTRY_URL;
160
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
161
+ const fetchImpl = opts.fetchImpl ?? fetch;
162
+ const warn = opts.warn ?? ((m) => console.warn(m));
163
+ const remote = await fetchRegistryVersion(url, timeoutMs, fetchImpl);
164
+ if (!remote) return { remoteVersion: null, skewed: false, warned: false };
165
+ if (!isLocalBehind(opts.localVersion, remote)) {
166
+ return { remoteVersion: remote, skewed: false, warned: false };
167
+ }
168
+ warn(
169
+ `[neatd] running neat.is@${opts.localVersion} but neat.is@${remote} is on npm \u2014 run \`npm install -g neat.is@latest\` to update`
170
+ );
171
+ return { remoteVersion: remote, skewed: true, warned: true };
172
+ }
173
+
110
174
  // src/neatd.ts
175
+ function localVersion() {
176
+ if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
177
+ return process.env.NEAT_LOCAL_VERSION;
178
+ }
179
+ try {
180
+ const req2 = createRequire(import.meta.url);
181
+ const pkg = req2("../package.json");
182
+ return typeof pkg.version === "string" ? pkg.version : "0.0.0";
183
+ } catch {
184
+ return "0.0.0";
185
+ }
186
+ }
111
187
  function neatHome() {
112
188
  if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
113
189
  return path2.resolve(process.env.NEAT_HOME);
@@ -137,6 +213,10 @@ async function cmdStart() {
137
213
  console.log(`neatd: registry at ${registryPath()}`);
138
214
  if (handle.restAddress) console.log(`neatd: REST \u2192 ${handle.restAddress}`);
139
215
  if (handle.otlpAddress) console.log(`neatd: OTLP \u2192 ${handle.otlpAddress}`);
216
+ if (process.env.NEAT_DISABLE_VERSION_CHECK !== "1") {
217
+ void checkVersionSkew({ localVersion: localVersion() }).catch(() => {
218
+ });
219
+ }
140
220
  const skipWeb = process.env.NEAT_WEB_DISABLED === "1";
141
221
  let web = null;
142
222
  if (!skipWeb) {
package/dist/neatd.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/neatd.ts","../src/web-spawn.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `neatd` — distribution-layer daemon CLI (ADR-049).\n *\n * Subcommands:\n * neatd start [--foreground] boot the daemon and watch the registry\n * neatd stop signal the running daemon to shut down\n * neatd reload signal the running daemon to re-read the registry\n * neatd status print PID + per-project last-seen timestamps\n *\n * MVP runs in foreground only. Backgrounding is the supervisor's job\n * (launchd / systemd / nohup) — `neatd start` blocks until SIGINT/SIGTERM.\n *\n * v0.2.10: also brings up the web UI on port 6328 by default (ADR-059).\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { startDaemon } from './daemon.js'\nimport { listProjects, registryPath } from './registry.js'\nimport { spawnWebUI, DEFAULT_WEB_PORT, type WebHandle } from './web-spawn.js'\n\nfunction neatHome(): string {\n if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {\n return path.resolve(process.env.NEAT_HOME)\n }\n const home = process.env.HOME ?? process.env.USERPROFILE ?? ''\n return path.join(home, '.neat')\n}\n\nasync function readPid(): Promise<number | null> {\n try {\n const raw = await fs.readFile(path.join(neatHome(), 'neatd.pid'), 'utf8')\n const n = Number.parseInt(raw.trim(), 10)\n return Number.isFinite(n) ? n : null\n } catch {\n return null\n }\n}\n\nfunction usage(): void {\n console.log('usage: neatd <start|stop|reload|status> [--foreground]')\n}\n\nfunction restPortFromEnv(): number {\n const raw = process.env.PORT\n return raw && raw.length > 0 ? Number.parseInt(raw, 10) : 8080\n}\n\nasync function cmdStart(): Promise<void> {\n const handle = await startDaemon()\n console.log(`neatd: started, PID ${process.pid}, ${handle.slots.size} project(s)`)\n console.log(`neatd: registry at ${registryPath()}`)\n // ADR-063 — surface the bound addresses so the operator can sanity-check\n // the documented happy path without grepping startup logs.\n if (handle.restAddress) console.log(`neatd: REST → ${handle.restAddress}`)\n if (handle.otlpAddress) console.log(`neatd: OTLP → ${handle.otlpAddress}`)\n\n // ADR-059 — bring up the web UI alongside the daemon. Failure here aborts\n // start with the clear-error pattern from ADR-049 instead of silently\n // running headless.\n const skipWeb = process.env.NEAT_WEB_DISABLED === '1'\n let web: WebHandle | null = null\n if (!skipWeb) {\n try {\n web = await spawnWebUI(restPortFromEnv())\n } catch (err) {\n console.error((err as Error).message)\n await handle.stop().catch(() => {})\n process.exit(3)\n }\n } else {\n console.log('neatd: web UI disabled (NEAT_WEB_DISABLED=1)')\n }\n\n console.log('neatd: SIGHUP reloads, SIGTERM/SIGINT stops')\n\n let stopping = false\n const shutdown = (signal: NodeJS.Signals): void => {\n if (stopping) return\n stopping = true\n console.log(`neatd: ${signal} received, stopping…`)\n void Promise.allSettled([handle.stop(), web ? web.stop() : Promise.resolve()])\n .catch((err) => console.error(`neatd: shutdown error — ${(err as Error).message}`))\n .finally(() => process.exit(0))\n }\n process.on('SIGTERM', shutdown)\n process.on('SIGINT', shutdown)\n\n // Block forever — supervisors keep us in the foreground.\n await new Promise<void>(() => {})\n}\n\nasync function cmdStop(): Promise<void> {\n const pid = await readPid()\n if (pid === null) {\n console.error('neatd: no running daemon found (no PID file)')\n process.exit(1)\n }\n try {\n process.kill(pid, 'SIGTERM')\n console.log(`neatd: SIGTERM sent to PID ${pid}`)\n } catch (err) {\n console.error(`neatd: failed to signal PID ${pid} — ${(err as Error).message}`)\n process.exit(1)\n }\n}\n\nasync function cmdReload(): Promise<void> {\n const pid = await readPid()\n if (pid === null) {\n console.error('neatd: no running daemon found (no PID file)')\n process.exit(1)\n }\n try {\n process.kill(pid, 'SIGHUP')\n console.log(`neatd: SIGHUP sent to PID ${pid}`)\n } catch (err) {\n console.error(`neatd: failed to signal PID ${pid} — ${(err as Error).message}`)\n process.exit(1)\n }\n}\n\nasync function cmdStatus(): Promise<void> {\n const pid = await readPid()\n console.log(`pid: ${pid ?? '(not running)'}`)\n console.log(`registry: ${registryPath()}`)\n const webPort = process.env.NEAT_WEB_PORT\n ? Number.parseInt(process.env.NEAT_WEB_PORT, 10)\n : DEFAULT_WEB_PORT\n console.log(`web ui: http://localhost:${webPort}`)\n const projects = await listProjects().catch(() => [])\n if (projects.length === 0) {\n console.log('projects: (none)')\n return\n }\n console.log('projects:')\n for (const p of projects) {\n const seen = p.lastSeenAt ?? 'never'\n console.log(` ${p.name}\\t${p.status}\\t${p.path}\\tlast-seen=${seen}`)\n }\n}\n\nasync function main(): Promise<void> {\n const cmd = process.argv[2]\n if (!cmd || cmd === '-h' || cmd === '--help') {\n usage()\n process.exit(cmd ? 0 : 2)\n }\n\n if (cmd === 'start') return cmdStart()\n if (cmd === 'stop') return cmdStop()\n if (cmd === 'reload') return cmdReload()\n if (cmd === 'status') return cmdStatus()\n\n console.error(`neatd: unknown command \"${cmd}\"`)\n usage()\n process.exit(1)\n}\n\nconst entry = process.argv[1] ?? ''\nif (/[\\\\/]neatd\\.(?:cjs|js)$/.test(entry) || entry.endsWith('/neatd')) {\n main().catch((err) => {\n console.error(err)\n process.exit(1)\n })\n}\n","/**\n * Web UI spawn helper (ADR-059).\n *\n * `neatd start` brings up the REST API + OTel receivers, then calls\n * `spawnWebUI(restPort)` to launch the Next.js web shell as a child process.\n * Lifecycle is parent-tied: SIGTERM/SIGINT on neatd cascades to the child\n * via `stop()`.\n *\n * Default port `6328` (NEAT in T9). Override with `NEAT_WEB_PORT`.\n * Hard-fails on collision so the operator never loses track of which URL to\n * open.\n */\n\nimport { spawn, type ChildProcess } from 'node:child_process'\nimport net from 'node:net'\nimport path from 'node:path'\n\nexport const DEFAULT_WEB_PORT = 6328\n\nexport interface WebHandle {\n child: ChildProcess\n port: number\n stop: () => Promise<void>\n}\n\n/**\n * Best-effort port collision check before spawning. Binds, closes, returns.\n * Race condition between the check and the actual `next start` is acceptable\n * — Next.js will then fail loudly and the parent exits.\n */\nasync function assertPortFree(port: number): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const tester = net.createServer()\n tester.once('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n reject(\n new Error(\n `neatd: web UI port ${port} in use; set NEAT_WEB_PORT to override or stop the conflicting process`,\n ),\n )\n } else {\n reject(err)\n }\n })\n tester.once('listening', () => {\n tester.close(() => resolve())\n })\n tester.listen(port, '127.0.0.1')\n })\n}\n\n/**\n * Resolve the web package directory. In a global install\n * (`npm install -g neat.is`) the package lives at\n * `node_modules/@neat.is/web/`; in the monorepo it's\n * `packages/web/`. `require.resolve` finds whichever one Node has on its\n * search path.\n */\nfunction resolveWebPackageDir(): string {\n const req = (typeof require !== 'undefined'\n ? require\n : // ESM fallback — daemon CJS bundle has `require`, but typecheck wants this\n (eval('require') as NodeRequire))\n const pkgJsonPath = req.resolve('@neat.is/web/package.json')\n return path.dirname(pkgJsonPath)\n}\n\n/**\n * Locate the standalone server.js inside the web package. ADR-064 #2 — the\n * tarball ships `.next/standalone/packages/web/server.js` (path preserved\n * relative to the monorepo tracing root). Missing means the package was\n * published without `next build` having run, which the smoke-test gate\n * catches at publish time.\n */\nfunction resolveStandaloneServerEntry(webDir: string): string {\n return path.join(webDir, '.next/standalone/packages/web/server.js')\n}\n\nexport async function spawnWebUI(restPort: number): Promise<WebHandle> {\n const portRaw = process.env.NEAT_WEB_PORT\n const port = portRaw && portRaw.length > 0 ? Number.parseInt(portRaw, 10) : DEFAULT_WEB_PORT\n if (!Number.isFinite(port) || port <= 0 || port > 65535) {\n throw new Error(`neatd: invalid NEAT_WEB_PORT=\"${portRaw}\"`)\n }\n\n await assertPortFree(port)\n\n const cwd = resolveWebPackageDir()\n const serverEntry = resolveStandaloneServerEntry(cwd)\n // ADR-064 — fail loudly if the standalone build is missing. v0.3.0 shipped\n // without it; the symptom on the user side was `next start` aborting with\n // `Could not find a production build in the '.next' directory`. This check\n // catches it at the parent's bootstrap instead of letting the child crash\n // a few moments later with a worse error.\n try {\n require.resolve(serverEntry)\n } catch {\n throw new Error(\n `neatd: web UI standalone build missing at ${serverEntry}. ` +\n `The published @neat.is/web tarball should include it; if you're running from a ` +\n `monorepo checkout, run \\`npm run build --workspace @neat.is/web\\` first, or set ` +\n `NEAT_WEB_DISABLED=1 to skip the web UI.`,\n )\n }\n\n // ADR-059 #6 — child inherits NEAT_API_URL pointing at our REST server,\n // unless the operator pre-configured it.\n const env: NodeJS.ProcessEnv = {\n ...process.env,\n PORT: String(port),\n HOSTNAME: process.env.HOSTNAME ?? '0.0.0.0',\n NEAT_API_URL: process.env.NEAT_API_URL ?? `http://localhost:${restPort}`,\n }\n\n // The standalone bundle is self-contained — its own `node_modules` and\n // `package.json` sit alongside `server.js`. Spawn `node` against it\n // directly; no `next start` (which needs the source tree + build cache)\n // and no `npm exec` (which is monorepo-only in practice).\n // `detached: false` keeps the child in our process group so signals reach it.\n const child = spawn(process.execPath, [serverEntry], {\n cwd: path.dirname(serverEntry),\n env,\n stdio: ['ignore', 'inherit', 'inherit'],\n detached: false,\n })\n\n child.on('error', (err) => {\n console.error(`neatd: web UI spawn error — ${err.message}`)\n })\n\n console.log(`neatd: web UI listening on http://localhost:${port}`)\n\n let stopped = false\n async function stop(): Promise<void> {\n if (stopped || !child.pid) return\n stopped = true\n try {\n child.kill('SIGTERM')\n } catch {\n /* already gone */\n }\n // Give the child up to 3s to exit gracefully, then SIGKILL.\n await new Promise<void>((resolve) => {\n const t = setTimeout(() => {\n try {\n child.kill('SIGKILL')\n } catch {\n /* gone */\n }\n resolve()\n }, 3000)\n child.once('exit', () => {\n clearTimeout(t)\n resolve()\n })\n })\n }\n\n return { child, port, stop }\n}\n"],"mappings":"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","names":["path","path"]}
1
+ {"version":3,"sources":["../src/neatd.ts","../src/web-spawn.ts","../src/version-skew.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `neatd` — distribution-layer daemon CLI (ADR-049).\n *\n * Subcommands:\n * neatd start [--foreground] boot the daemon and watch the registry\n * neatd stop signal the running daemon to shut down\n * neatd reload signal the running daemon to re-read the registry\n * neatd status print PID + per-project last-seen timestamps\n *\n * MVP runs in foreground only. Backgrounding is the supervisor's job\n * (launchd / systemd / nohup) — `neatd start` blocks until SIGINT/SIGTERM.\n *\n * v0.2.10: also brings up the web UI on port 6328 by default (ADR-059).\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { createRequire } from 'node:module'\nimport { startDaemon } from './daemon.js'\nimport { listProjects, registryPath } from './registry.js'\nimport { spawnWebUI, DEFAULT_WEB_PORT, type WebHandle } from './web-spawn.js'\nimport { checkVersionSkew } from './version-skew.js'\n\n// Resolve the running @neat.is/core version from the bundled package.json.\n// Lockstep publishing (ADR-052) keeps this in step with the `neat.is` meta\n// package, which is what the operator installs globally. Tests stub by\n// setting NEAT_LOCAL_VERSION.\nfunction localVersion(): string {\n if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {\n return process.env.NEAT_LOCAL_VERSION\n }\n try {\n const req = createRequire(import.meta.url)\n const pkg = req('../package.json') as { version?: string }\n return typeof pkg.version === 'string' ? pkg.version : '0.0.0'\n } catch {\n return '0.0.0'\n }\n}\n\nfunction neatHome(): string {\n if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {\n return path.resolve(process.env.NEAT_HOME)\n }\n const home = process.env.HOME ?? process.env.USERPROFILE ?? ''\n return path.join(home, '.neat')\n}\n\nasync function readPid(): Promise<number | null> {\n try {\n const raw = await fs.readFile(path.join(neatHome(), 'neatd.pid'), 'utf8')\n const n = Number.parseInt(raw.trim(), 10)\n return Number.isFinite(n) ? n : null\n } catch {\n return null\n }\n}\n\nfunction usage(): void {\n console.log('usage: neatd <start|stop|reload|status> [--foreground]')\n}\n\nfunction restPortFromEnv(): number {\n const raw = process.env.PORT\n return raw && raw.length > 0 ? Number.parseInt(raw, 10) : 8080\n}\n\nasync function cmdStart(): Promise<void> {\n const handle = await startDaemon()\n console.log(`neatd: started, PID ${process.pid}, ${handle.slots.size} project(s)`)\n console.log(`neatd: registry at ${registryPath()}`)\n // ADR-063 — surface the bound addresses so the operator can sanity-check\n // the documented happy path without grepping startup logs.\n if (handle.restAddress) console.log(`neatd: REST → ${handle.restAddress}`)\n if (handle.otlpAddress) console.log(`neatd: OTLP → ${handle.otlpAddress}`)\n\n // Version-skew advisory — non-fatal, fail-open. Surfaces the gap when the\n // operator's globally installed `neat.is` binary lags the published\n // version. Set NEAT_DISABLE_VERSION_CHECK=1 to silence the check (e.g. in\n // air-gapped environments).\n if (process.env.NEAT_DISABLE_VERSION_CHECK !== '1') {\n void checkVersionSkew({ localVersion: localVersion() }).catch(() => {\n // Fail-open: any thrown error from the helper resolves silently. The\n // helper already catches its own internals; this catches anything\n // exotic the Promise chain bubbles up.\n })\n }\n\n // ADR-059 — bring up the web UI alongside the daemon. Failure here aborts\n // start with the clear-error pattern from ADR-049 instead of silently\n // running headless.\n const skipWeb = process.env.NEAT_WEB_DISABLED === '1'\n let web: WebHandle | null = null\n if (!skipWeb) {\n try {\n web = await spawnWebUI(restPortFromEnv())\n } catch (err) {\n console.error((err as Error).message)\n await handle.stop().catch(() => {})\n process.exit(3)\n }\n } else {\n console.log('neatd: web UI disabled (NEAT_WEB_DISABLED=1)')\n }\n\n console.log('neatd: SIGHUP reloads, SIGTERM/SIGINT stops')\n\n let stopping = false\n const shutdown = (signal: NodeJS.Signals): void => {\n if (stopping) return\n stopping = true\n console.log(`neatd: ${signal} received, stopping…`)\n void Promise.allSettled([handle.stop(), web ? web.stop() : Promise.resolve()])\n .catch((err) => console.error(`neatd: shutdown error — ${(err as Error).message}`))\n .finally(() => process.exit(0))\n }\n process.on('SIGTERM', shutdown)\n process.on('SIGINT', shutdown)\n\n // Block forever — supervisors keep us in the foreground.\n await new Promise<void>(() => {})\n}\n\nasync function cmdStop(): Promise<void> {\n const pid = await readPid()\n if (pid === null) {\n console.error('neatd: no running daemon found (no PID file)')\n process.exit(1)\n }\n try {\n process.kill(pid, 'SIGTERM')\n console.log(`neatd: SIGTERM sent to PID ${pid}`)\n } catch (err) {\n console.error(`neatd: failed to signal PID ${pid} — ${(err as Error).message}`)\n process.exit(1)\n }\n}\n\nasync function cmdReload(): Promise<void> {\n const pid = await readPid()\n if (pid === null) {\n console.error('neatd: no running daemon found (no PID file)')\n process.exit(1)\n }\n try {\n process.kill(pid, 'SIGHUP')\n console.log(`neatd: SIGHUP sent to PID ${pid}`)\n } catch (err) {\n console.error(`neatd: failed to signal PID ${pid} — ${(err as Error).message}`)\n process.exit(1)\n }\n}\n\nasync function cmdStatus(): Promise<void> {\n const pid = await readPid()\n console.log(`pid: ${pid ?? '(not running)'}`)\n console.log(`registry: ${registryPath()}`)\n const webPort = process.env.NEAT_WEB_PORT\n ? Number.parseInt(process.env.NEAT_WEB_PORT, 10)\n : DEFAULT_WEB_PORT\n console.log(`web ui: http://localhost:${webPort}`)\n const projects = await listProjects().catch(() => [])\n if (projects.length === 0) {\n console.log('projects: (none)')\n return\n }\n console.log('projects:')\n for (const p of projects) {\n const seen = p.lastSeenAt ?? 'never'\n console.log(` ${p.name}\\t${p.status}\\t${p.path}\\tlast-seen=${seen}`)\n }\n}\n\nasync function main(): Promise<void> {\n const cmd = process.argv[2]\n if (!cmd || cmd === '-h' || cmd === '--help') {\n usage()\n process.exit(cmd ? 0 : 2)\n }\n\n if (cmd === 'start') return cmdStart()\n if (cmd === 'stop') return cmdStop()\n if (cmd === 'reload') return cmdReload()\n if (cmd === 'status') return cmdStatus()\n\n console.error(`neatd: unknown command \"${cmd}\"`)\n usage()\n process.exit(1)\n}\n\nconst entry = process.argv[1] ?? ''\nif (/[\\\\/]neatd\\.(?:cjs|js)$/.test(entry) || entry.endsWith('/neatd')) {\n main().catch((err) => {\n console.error(err)\n process.exit(1)\n })\n}\n","/**\n * Web UI spawn helper (ADR-059).\n *\n * `neatd start` brings up the REST API + OTel receivers, then calls\n * `spawnWebUI(restPort)` to launch the Next.js web shell as a child process.\n * Lifecycle is parent-tied: SIGTERM/SIGINT on neatd cascades to the child\n * via `stop()`.\n *\n * Default port `6328` (NEAT in T9). Override with `NEAT_WEB_PORT`.\n * Hard-fails on collision so the operator never loses track of which URL to\n * open.\n */\n\nimport { spawn, type ChildProcess } from 'node:child_process'\nimport net from 'node:net'\nimport path from 'node:path'\n\nexport const DEFAULT_WEB_PORT = 6328\n\nexport interface WebHandle {\n child: ChildProcess\n port: number\n stop: () => Promise<void>\n}\n\n/**\n * Best-effort port collision check before spawning. Binds, closes, returns.\n * Race condition between the check and the actual `next start` is acceptable\n * — Next.js will then fail loudly and the parent exits.\n */\nasync function assertPortFree(port: number): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const tester = net.createServer()\n tester.once('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n reject(\n new Error(\n `neatd: web UI port ${port} in use; set NEAT_WEB_PORT to override or stop the conflicting process`,\n ),\n )\n } else {\n reject(err)\n }\n })\n tester.once('listening', () => {\n tester.close(() => resolve())\n })\n tester.listen(port, '127.0.0.1')\n })\n}\n\n/**\n * Resolve the web package directory. In a global install\n * (`npm install -g neat.is`) the package lives at\n * `node_modules/@neat.is/web/`; in the monorepo it's\n * `packages/web/`. `require.resolve` finds whichever one Node has on its\n * search path.\n */\nfunction resolveWebPackageDir(): string {\n const req = (typeof require !== 'undefined'\n ? require\n : // ESM fallback — daemon CJS bundle has `require`, but typecheck wants this\n (eval('require') as NodeRequire))\n const pkgJsonPath = req.resolve('@neat.is/web/package.json')\n return path.dirname(pkgJsonPath)\n}\n\n/**\n * Locate the standalone server.js inside the web package. ADR-064 #2 — the\n * tarball ships `.next/standalone/packages/web/server.js` (path preserved\n * relative to the monorepo tracing root). Missing means the package was\n * published without `next build` having run, which the smoke-test gate\n * catches at publish time.\n */\nfunction resolveStandaloneServerEntry(webDir: string): string {\n return path.join(webDir, '.next/standalone/packages/web/server.js')\n}\n\nexport async function spawnWebUI(restPort: number): Promise<WebHandle> {\n const portRaw = process.env.NEAT_WEB_PORT\n const port = portRaw && portRaw.length > 0 ? Number.parseInt(portRaw, 10) : DEFAULT_WEB_PORT\n if (!Number.isFinite(port) || port <= 0 || port > 65535) {\n throw new Error(`neatd: invalid NEAT_WEB_PORT=\"${portRaw}\"`)\n }\n\n await assertPortFree(port)\n\n const cwd = resolveWebPackageDir()\n const serverEntry = resolveStandaloneServerEntry(cwd)\n // ADR-064 — fail loudly if the standalone build is missing. v0.3.0 shipped\n // without it; the symptom on the user side was `next start` aborting with\n // `Could not find a production build in the '.next' directory`. This check\n // catches it at the parent's bootstrap instead of letting the child crash\n // a few moments later with a worse error.\n try {\n require.resolve(serverEntry)\n } catch {\n throw new Error(\n `neatd: web UI standalone build missing at ${serverEntry}. ` +\n `The published @neat.is/web tarball should include it; if you're running from a ` +\n `monorepo checkout, run \\`npm run build --workspace @neat.is/web\\` first, or set ` +\n `NEAT_WEB_DISABLED=1 to skip the web UI.`,\n )\n }\n\n // ADR-059 #6 — child inherits NEAT_API_URL pointing at our REST server,\n // unless the operator pre-configured it.\n const env: NodeJS.ProcessEnv = {\n ...process.env,\n PORT: String(port),\n HOSTNAME: process.env.HOSTNAME ?? '0.0.0.0',\n NEAT_API_URL: process.env.NEAT_API_URL ?? `http://localhost:${restPort}`,\n }\n\n // The standalone bundle is self-contained — its own `node_modules` and\n // `package.json` sit alongside `server.js`. Spawn `node` against it\n // directly; no `next start` (which needs the source tree + build cache)\n // and no `npm exec` (which is monorepo-only in practice).\n // `detached: false` keeps the child in our process group so signals reach it.\n const child = spawn(process.execPath, [serverEntry], {\n cwd: path.dirname(serverEntry),\n env,\n stdio: ['ignore', 'inherit', 'inherit'],\n detached: false,\n })\n\n child.on('error', (err) => {\n console.error(`neatd: web UI spawn error — ${err.message}`)\n })\n\n console.log(`neatd: web UI listening on http://localhost:${port}`)\n\n let stopped = false\n async function stop(): Promise<void> {\n if (stopped || !child.pid) return\n stopped = true\n try {\n child.kill('SIGTERM')\n } catch {\n /* already gone */\n }\n // Give the child up to 3s to exit gracefully, then SIGKILL.\n await new Promise<void>((resolve) => {\n const t = setTimeout(() => {\n try {\n child.kill('SIGKILL')\n } catch {\n /* gone */\n }\n resolve()\n }, 3000)\n child.once('exit', () => {\n clearTimeout(t)\n resolve()\n })\n })\n }\n\n return { child, port, stop }\n}\n","/**\n * Version-skew check for `neatd start` (ADR-049 §Lifecycle — non-fatal\n * advisory).\n *\n * A globally installed `neat.is` binary may run an older version than the\n * `neat.is` available on npm — happens when the operator has not run\n * `npm install -g neat.is@latest` since the last publish. The six packages\n * move in lockstep (publish-system contract / ADR-052), so the registry\n * version of `neat.is` mirrors the local `@neat.is/core` version.\n *\n * On daemon start we compare the two and log a single advisory line when\n * the registry is ahead. The check is fail-open by design — registry\n * unreachable, slow, or returning an unexpected payload all resolve to\n * \"no warning, daemon proceeds normally.\"\n */\n\nimport { setTimeout as delay } from 'node:timers/promises'\n\nconst NPM_REGISTRY_URL = 'https://registry.npmjs.org/neat.is/latest'\nconst DEFAULT_TIMEOUT_MS = 2000\n\nexport interface VersionSkewCheckOptions {\n // The local version the running daemon reports. Production: read from the\n // bundled @neat.is/core package.json. Tests pass a literal.\n localVersion: string\n // Override the registry URL (tests point at a mock server).\n registryUrl?: string\n // Timeout for the registry fetch. Defaults to 2s.\n timeoutMs?: number\n // Injected so tests can simulate slow responses without real timers.\n fetchImpl?: typeof fetch\n // Sink for the advisory line. Defaults to console.warn.\n warn?: (message: string) => void\n}\n\nexport interface VersionSkewCheckResult {\n // The version the registry reported, or null on any fetch / parse failure.\n remoteVersion: string | null\n // Whether the local version is behind the registry's.\n skewed: boolean\n // Whether the advisory was emitted.\n warned: boolean\n}\n\n/**\n * Fail-open registry lookup. Returns the version string or null on any\n * non-success path (network, timeout, parse, missing field).\n */\nasync function fetchRegistryVersion(\n url: string,\n timeoutMs: number,\n fetchImpl: typeof fetch,\n): Promise<string | null> {\n const controller = new AbortController()\n let timedOut = false\n const timer = delay(timeoutMs).then(() => {\n timedOut = true\n controller.abort()\n })\n try {\n const res = await Promise.race([\n fetchImpl(url, { signal: controller.signal, headers: { accept: 'application/json' } }),\n timer.then(() => null),\n ])\n if (timedOut || !res) return null\n if (!res.ok) return null\n const body = (await res.json()) as { version?: unknown }\n if (typeof body.version !== 'string' || body.version.length === 0) return null\n return body.version\n } catch {\n return null\n } finally {\n // Make sure the abort fires so the delay promise resolves even on\n // success — keeps the process from hanging on test teardown.\n if (!timedOut) controller.abort()\n }\n}\n\n/**\n * Compare two semver-shaped strings. Returns true when `local` is strictly\n * older than `remote`. We don't use a real semver library because the\n * advisory is intentionally coarse — any non-equal pair where the registry\n * looks newer is enough to suggest a re-install. The implementation does a\n * plain segment-by-segment integer compare and tolerates pre-release\n * suffixes by stripping them off.\n */\nexport function isLocalBehind(local: string, remote: string): boolean {\n if (local === remote) return false\n const parse = (v: string): number[] => {\n const core = v.split(/[-+]/)[0] ?? v\n return core.split('.').map((s) => {\n const n = Number.parseInt(s, 10)\n return Number.isFinite(n) ? n : 0\n })\n }\n const a = parse(local)\n const b = parse(remote)\n const len = Math.max(a.length, b.length)\n for (let i = 0; i < len; i++) {\n const av = a[i] ?? 0\n const bv = b[i] ?? 0\n if (av < bv) return true\n if (av > bv) return false\n }\n return false\n}\n\nexport async function checkVersionSkew(\n opts: VersionSkewCheckOptions,\n): Promise<VersionSkewCheckResult> {\n const url = opts.registryUrl ?? NPM_REGISTRY_URL\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const fetchImpl = opts.fetchImpl ?? fetch\n const warn = opts.warn ?? ((m) => console.warn(m))\n\n const remote = await fetchRegistryVersion(url, timeoutMs, fetchImpl)\n if (!remote) return { remoteVersion: null, skewed: false, warned: false }\n\n if (!isLocalBehind(opts.localVersion, remote)) {\n return { remoteVersion: remote, skewed: false, warned: false }\n }\n\n warn(\n `[neatd] running neat.is@${opts.localVersion} but neat.is@${remote} is on npm — run \\`npm install -g neat.is@latest\\` to update`,\n )\n return { remoteVersion: remote, skewed: true, warned: true }\n}\n"],"mappings":"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","names":["path","req","path"]}
package/dist/server.cjs CHANGED
@@ -1903,7 +1903,7 @@ async function handleSpan(ctx, span) {
1903
1903
  service: span.service,
1904
1904
  traceId: span.traceId,
1905
1905
  spanId: span.spanId,
1906
- errorMessage: span.exception?.message ?? span.name ?? "unknown error",
1906
+ errorMessage: span.exception?.message ?? "unknown error",
1907
1907
  ...span.exception?.type ? { exceptionType: span.exception.type } : {},
1908
1908
  ...span.exception?.stacktrace ? { exceptionStacktrace: span.exception.stacktrace } : {},
1909
1909
  ...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},