@neat.is/core 0.7.0 → 0.7.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/dist/{chunk-Q6DPK3RA.js → chunk-6H757ZNM.js} +2 -2
- package/dist/{chunk-UBQ4ZZT3.js → chunk-MDBE23Y3.js} +3 -3
- package/dist/{chunk-N5L3RBGP.js → chunk-P2ZEKJ35.js} +44 -2
- package/dist/chunk-P2ZEKJ35.js.map +1 -0
- package/dist/{chunk-5RIL3U5A.js → chunk-RR4LWQQB.js} +516 -229
- package/dist/chunk-RR4LWQQB.js.map +1 -0
- package/dist/cli.cjs +1351 -653
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +2 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +379 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +810 -480
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +819 -489
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-ZNC2QED2.js → otel-grpc-APVZSB6W.js} +3 -3
- package/dist/server.cjs +840 -510
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-5RIL3U5A.js.map +0 -1
- package/dist/chunk-N5L3RBGP.js.map +0 -1
- /package/dist/{chunk-Q6DPK3RA.js.map → chunk-6H757ZNM.js.map} +0 -0
- /package/dist/{chunk-UBQ4ZZT3.js.map → chunk-MDBE23Y3.js.map} +0 -0
- /package/dist/{otel-grpc-ZNC2QED2.js.map → otel-grpc-APVZSB6W.js.map} +0 -0
package/dist/cli.d.cts
CHANGED
|
@@ -80,5 +80,6 @@ declare class ProjectResolutionError extends Error {
|
|
|
80
80
|
declare function resolveProjectForVerb(client: HttpClient, parsed: ParsedArgs): Promise<string | undefined>;
|
|
81
81
|
declare function resolveDaemonUrl(project?: string): Promise<string>;
|
|
82
82
|
declare function runQueryVerb(cmd: string, parsed: ParsedArgs): Promise<number>;
|
|
83
|
+
declare function runMonitorVerb(parsed: ParsedArgs): Promise<number>;
|
|
83
84
|
|
|
84
|
-
export { CLAUDE_SKILL_CONFIG, type InitOptions, type InitResult, ProjectResolutionError, QUERY_VERBS, type SkillOptions, commandPrefix, isNpxInvocation, main, parseArgs, printBanner, readPackageVersion, resolveDaemonUrl, resolveProjectForVerb, runInit, runQueryVerb, runSkill, usage };
|
|
85
|
+
export { CLAUDE_SKILL_CONFIG, type InitOptions, type InitResult, ProjectResolutionError, QUERY_VERBS, type SkillOptions, commandPrefix, isNpxInvocation, main, parseArgs, printBanner, readPackageVersion, resolveDaemonUrl, resolveProjectForVerb, runInit, runMonitorVerb, runQueryVerb, runSkill, usage };
|
package/dist/cli.d.ts
CHANGED
|
@@ -80,5 +80,6 @@ declare class ProjectResolutionError extends Error {
|
|
|
80
80
|
declare function resolveProjectForVerb(client: HttpClient, parsed: ParsedArgs): Promise<string | undefined>;
|
|
81
81
|
declare function resolveDaemonUrl(project?: string): Promise<string>;
|
|
82
82
|
declare function runQueryVerb(cmd: string, parsed: ParsedArgs): Promise<number>;
|
|
83
|
+
declare function runMonitorVerb(parsed: ParsedArgs): Promise<number>;
|
|
83
84
|
|
|
84
|
-
export { CLAUDE_SKILL_CONFIG, type InitOptions, type InitResult, ProjectResolutionError, QUERY_VERBS, type SkillOptions, commandPrefix, isNpxInvocation, main, parseArgs, printBanner, readPackageVersion, resolveDaemonUrl, resolveProjectForVerb, runInit, runQueryVerb, runSkill, usage };
|
|
85
|
+
export { CLAUDE_SKILL_CONFIG, type InitOptions, type InitResult, ProjectResolutionError, QUERY_VERBS, type SkillOptions, commandPrefix, isNpxInvocation, main, parseArgs, printBanner, readPackageVersion, resolveDaemonUrl, resolveProjectForVerb, runInit, runMonitorVerb, runQueryVerb, runSkill, usage };
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
resolveHost,
|
|
7
7
|
resolveNeatVersion,
|
|
8
8
|
writeDaemonRecord
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MDBE23Y3.js";
|
|
10
10
|
import {
|
|
11
11
|
buildSearchIndex
|
|
12
12
|
} from "./chunk-BIY46Q6U.js";
|
|
@@ -72,10 +72,10 @@ import {
|
|
|
72
72
|
startStalenessLoop,
|
|
73
73
|
upsertConnectorEntry,
|
|
74
74
|
validateConnectorEntry
|
|
75
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-RR4LWQQB.js";
|
|
76
76
|
import {
|
|
77
77
|
startOtelGrpcReceiver
|
|
78
|
-
} from "./chunk-
|
|
78
|
+
} from "./chunk-6H757ZNM.js";
|
|
79
79
|
import {
|
|
80
80
|
__dirname,
|
|
81
81
|
__require,
|
|
@@ -83,7 +83,7 @@ import {
|
|
|
83
83
|
buildOtelReceiver,
|
|
84
84
|
listenSteppingOtlp,
|
|
85
85
|
readAuthEnv
|
|
86
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-P2ZEKJ35.js";
|
|
87
87
|
|
|
88
88
|
// src/cli.ts
|
|
89
89
|
import path11 from "path";
|
|
@@ -825,7 +825,7 @@ import semver from "semver";
|
|
|
825
825
|
|
|
826
826
|
// src/installers/templates.ts
|
|
827
827
|
var OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-069). OpenTelemetry auto-instrumentation hook.";
|
|
828
|
-
var OTEL_INIT_STAMP = "// neat-template-version:
|
|
828
|
+
var OTEL_INIT_STAMP = "// neat-template-version: 8 \u2014 CommonJS TypeScript uses a synchronous bootstrap (#910); OTel init degrades to no-OBSERVED when dependencies are absent (#820); daemon.json endpoint resolution (ADR-096) + layered file-first capture (ADR-090).";
|
|
829
829
|
var OTEL_OTLP_HEADERS_JS = "if (process.env.NEAT_OTEL_TOKEN) process.env.OTEL_EXPORTER_OTLP_HEADERS ||= 'Authorization=Bearer ' + process.env.NEAT_OTEL_TOKEN";
|
|
830
830
|
var OTEL_OTLP_PROTOCOL_JS = "process.env.OTEL_EXPORTER_OTLP_PROTOCOL ||= 'http/json'";
|
|
831
831
|
var OTEL_ENDPOINT_RESOLVER_CJS = `;(function () {
|
|
@@ -1189,6 +1189,13 @@ ${neatWireCaptureSource(false)}
|
|
|
1189
1189
|
}
|
|
1190
1190
|
}
|
|
1191
1191
|
`;
|
|
1192
|
+
var OTEL_INIT_TS_CJS = OTEL_INIT_CJS.replace(
|
|
1193
|
+
`${OTEL_INIT_HEADER}
|
|
1194
|
+
${OTEL_INIT_STAMP}`,
|
|
1195
|
+
`${OTEL_INIT_HEADER}
|
|
1196
|
+
${OTEL_INIT_STAMP}
|
|
1197
|
+
// @ts-nocheck \u2014 generated CommonJS runtime shim.`
|
|
1198
|
+
);
|
|
1192
1199
|
var OTEL_INIT_ESM = `${OTEL_INIT_HEADER}
|
|
1193
1200
|
${OTEL_INIT_STAMP}
|
|
1194
1201
|
${OTEL_ESM_NODE_IMPORTS}
|
|
@@ -1460,6 +1467,13 @@ function detectNonBundledInstrumentations(pkg) {
|
|
|
1460
1467
|
registration: "instrumentations.push(new (require('@prisma/instrumentation').PrismaInstrumentation)())"
|
|
1461
1468
|
});
|
|
1462
1469
|
}
|
|
1470
|
+
if (getMajor(deps["@nestjs/core"]) >= 11) {
|
|
1471
|
+
out.push({
|
|
1472
|
+
pkg: "@opentelemetry/instrumentation-nestjs-core",
|
|
1473
|
+
version: "^0.67.0",
|
|
1474
|
+
registration: "instrumentations.push(new (require('@opentelemetry/instrumentation-nestjs-core').NestInstrumentation)())"
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1463
1477
|
return out;
|
|
1464
1478
|
}
|
|
1465
1479
|
var OTEL_ENV = {
|
|
@@ -1772,18 +1786,19 @@ async function resolveEntry(serviceDir, pkg) {
|
|
|
1772
1786
|
}
|
|
1773
1787
|
function dispatchEntry(entryFile, pkg) {
|
|
1774
1788
|
const ext = path5.extname(entryFile).toLowerCase();
|
|
1775
|
-
if (ext === ".ts" || ext === ".tsx") return "ts";
|
|
1789
|
+
if (ext === ".ts" || ext === ".tsx") return pkg.type === "module" ? "ts" : "ts-cjs";
|
|
1776
1790
|
if (ext === ".mjs") return "esm";
|
|
1777
1791
|
if (ext === ".cjs") return "cjs";
|
|
1778
1792
|
return pkg.type === "module" ? "esm" : "cjs";
|
|
1779
1793
|
}
|
|
1780
1794
|
function otelInitFilename(flavor) {
|
|
1781
|
-
if (flavor === "ts") return "otel-init.ts";
|
|
1795
|
+
if (flavor === "ts" || flavor === "ts-cjs") return "otel-init.ts";
|
|
1782
1796
|
if (flavor === "esm") return "otel-init.mjs";
|
|
1783
1797
|
return "otel-init.cjs";
|
|
1784
1798
|
}
|
|
1785
1799
|
function otelInitContents(flavor) {
|
|
1786
1800
|
if (flavor === "ts") return OTEL_INIT_TS;
|
|
1801
|
+
if (flavor === "ts-cjs") return OTEL_INIT_TS_CJS;
|
|
1787
1802
|
if (flavor === "esm") return OTEL_INIT_ESM;
|
|
1788
1803
|
return OTEL_INIT_CJS;
|
|
1789
1804
|
}
|
|
@@ -3575,8 +3590,8 @@ async function healthIsForProject(restPort, project) {
|
|
|
3575
3590
|
}
|
|
3576
3591
|
return false;
|
|
3577
3592
|
}
|
|
3578
|
-
function daemonLogPath(
|
|
3579
|
-
return path8.join(
|
|
3593
|
+
function daemonLogPath(projectPath3) {
|
|
3594
|
+
return path8.join(projectPath3, "neat-out", "daemon.log");
|
|
3580
3595
|
}
|
|
3581
3596
|
function spawnDaemonDetached(spec) {
|
|
3582
3597
|
const here = path8.dirname(fileURLToPath2(import.meta.url));
|
|
@@ -4440,8 +4455,8 @@ async function runHooksCommand(args) {
|
|
|
4440
4455
|
}
|
|
4441
4456
|
}
|
|
4442
4457
|
|
|
4443
|
-
// src/
|
|
4444
|
-
import
|
|
4458
|
+
// src/monitor.ts
|
|
4459
|
+
import { EdgeType, parseEdgeId, Provenance as Provenance3 } from "@neat.is/types";
|
|
4445
4460
|
|
|
4446
4461
|
// src/cli-client.ts
|
|
4447
4462
|
import { Provenance as Provenance2 } from "@neat.is/types";
|
|
@@ -4884,6 +4899,9 @@ function formatDivergenceLine(d) {
|
|
|
4884
4899
|
switch (d.type) {
|
|
4885
4900
|
case "missing-observed":
|
|
4886
4901
|
case "missing-extracted":
|
|
4902
|
+
if (d.column) {
|
|
4903
|
+
return ` \u2022 [${d.type}] ${d.table ?? d.source} column ${d.column} \u2014 confidence ${d.confidence.toFixed(2)}`;
|
|
4904
|
+
}
|
|
4887
4905
|
return ` \u2022 [${d.type}] ${d.source} \u2192 ${d.target} (${d.edgeType}) \u2014 confidence ${d.confidence.toFixed(2)}`;
|
|
4888
4906
|
case "version-mismatch":
|
|
4889
4907
|
return ` \u2022 [${d.type}] ${d.source} \u2192 ${d.target} \u2014 declared ${d.extractedVersion}, observed engine ${d.observedVersion} (${d.compatibility})`;
|
|
@@ -4970,7 +4988,310 @@ async function pushSnapshotToRemote(input) {
|
|
|
4970
4988
|
);
|
|
4971
4989
|
}
|
|
4972
4990
|
|
|
4991
|
+
// src/monitor.ts
|
|
4992
|
+
var OBSERVED_DEP_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
4993
|
+
EdgeType.CALLS,
|
|
4994
|
+
EdgeType.CONNECTS_TO,
|
|
4995
|
+
EdgeType.PUBLISHES_TO,
|
|
4996
|
+
EdgeType.CONSUMES_FROM
|
|
4997
|
+
]);
|
|
4998
|
+
function divergenceKey(d) {
|
|
4999
|
+
const column = "column" in d && d.column ? d.column : "";
|
|
5000
|
+
const edgeType = "edgeType" in d && d.edgeType ? d.edgeType : "";
|
|
5001
|
+
let extra = "";
|
|
5002
|
+
switch (d.type) {
|
|
5003
|
+
case "version-mismatch":
|
|
5004
|
+
extra = `${d.extractedVersion}->${d.observedVersion}`;
|
|
5005
|
+
break;
|
|
5006
|
+
case "host-mismatch":
|
|
5007
|
+
extra = `${d.extractedHost}->${d.observedHost}`;
|
|
5008
|
+
break;
|
|
5009
|
+
case "compat-violation":
|
|
5010
|
+
extra = `${d.rule.kind}:${d.rule.package ?? ""}`;
|
|
5011
|
+
break;
|
|
5012
|
+
default:
|
|
5013
|
+
extra = "";
|
|
5014
|
+
}
|
|
5015
|
+
return `div|${d.type}|${d.source}|${d.target}|${edgeType}|${column}|${extra}`;
|
|
5016
|
+
}
|
|
5017
|
+
function tableLabel(d) {
|
|
5018
|
+
return "table" in d && d.table ? d.table : d.source;
|
|
5019
|
+
}
|
|
5020
|
+
function formatDivergenceLine2(d) {
|
|
5021
|
+
switch (d.type) {
|
|
5022
|
+
case "missing-observed":
|
|
5023
|
+
if (d.column) {
|
|
5024
|
+
return `\u26A0 divergence [missing-observed] ${tableLabel(d)}.${d.column} declared, never observed in production`;
|
|
5025
|
+
}
|
|
5026
|
+
return `\u26A0 divergence [missing-observed] ${d.source} \u2192 ${d.target} declared, never observed in production`;
|
|
5027
|
+
case "missing-extracted":
|
|
5028
|
+
if (d.column) {
|
|
5029
|
+
return `\u26A0 divergence [missing-extracted] production writes ${tableLabel(d)}.${d.column} \u2014 not declared in code`;
|
|
5030
|
+
}
|
|
5031
|
+
return `\u26A0 divergence [missing-extracted] production ${d.source} \u2192 ${d.target} \u2014 not declared in code`;
|
|
5032
|
+
case "version-mismatch":
|
|
5033
|
+
return `\u26A0 divergence [version-mismatch] ${d.source} \u2192 ${d.target} declared ${d.extractedVersion}, observed ${d.observedVersion} (${d.compatibility})`;
|
|
5034
|
+
case "host-mismatch":
|
|
5035
|
+
return `\u26A0 divergence [host-mismatch] ${d.source} \u2192 ${d.target} declared host ${d.extractedHost}, observed host ${d.observedHost}`;
|
|
5036
|
+
case "compat-violation":
|
|
5037
|
+
return `\u26A0 divergence [compat-violation] ${d.source} \u2192 ${d.target} \u2014 ${d.rule.kind}${d.rule.package ? ` (${d.rule.package})` : ""}`;
|
|
5038
|
+
}
|
|
5039
|
+
}
|
|
5040
|
+
function formatStaleLine(edgeId) {
|
|
5041
|
+
const parsed = parseEdgeId(edgeId);
|
|
5042
|
+
if (parsed) {
|
|
5043
|
+
return `\u22EF stale ${parsed.source} \u2192 ${parsed.target} (observed edge went quiet)`;
|
|
5044
|
+
}
|
|
5045
|
+
return `\u22EF stale ${edgeId} (observed edge went quiet)`;
|
|
5046
|
+
}
|
|
5047
|
+
function formatObservedEdgeLine(edge) {
|
|
5048
|
+
return `+ observed ${edge.source} \u2192 ${edge.target} (new runtime dependency)`;
|
|
5049
|
+
}
|
|
5050
|
+
function divergenceJson(d) {
|
|
5051
|
+
return JSON.stringify({ kind: "divergence", ...d });
|
|
5052
|
+
}
|
|
5053
|
+
function staleJson(edgeId) {
|
|
5054
|
+
const parsed = parseEdgeId(edgeId);
|
|
5055
|
+
return JSON.stringify({
|
|
5056
|
+
kind: "stale",
|
|
5057
|
+
edgeId,
|
|
5058
|
+
...parsed ? { source: parsed.source, target: parsed.target, edgeType: parsed.type } : {}
|
|
5059
|
+
});
|
|
5060
|
+
}
|
|
5061
|
+
function observedEdgeJson(edge) {
|
|
5062
|
+
return JSON.stringify({
|
|
5063
|
+
kind: "observed",
|
|
5064
|
+
id: edge.id,
|
|
5065
|
+
source: edge.source,
|
|
5066
|
+
target: edge.target,
|
|
5067
|
+
edgeType: edge.type,
|
|
5068
|
+
provenance: edge.provenance
|
|
5069
|
+
});
|
|
5070
|
+
}
|
|
5071
|
+
var MonitorEmitter = class {
|
|
5072
|
+
constructor(opts) {
|
|
5073
|
+
this.opts = opts;
|
|
5074
|
+
}
|
|
5075
|
+
opts;
|
|
5076
|
+
seen = /* @__PURE__ */ new Set();
|
|
5077
|
+
out(line) {
|
|
5078
|
+
this.opts.write(line + "\n");
|
|
5079
|
+
}
|
|
5080
|
+
// Emit every not-yet-seen divergence in a fresh result. Returns the count
|
|
5081
|
+
// newly emitted (0 → nothing printed, the silent path). Idempotent across
|
|
5082
|
+
// re-reads: the seen-set keys off divergenceKey.
|
|
5083
|
+
emitDivergences(result) {
|
|
5084
|
+
let emitted = 0;
|
|
5085
|
+
for (const d of result.divergences) {
|
|
5086
|
+
const key = divergenceKey(d);
|
|
5087
|
+
if (this.seen.has(key)) continue;
|
|
5088
|
+
this.seen.add(key);
|
|
5089
|
+
this.out(this.opts.json ? divergenceJson(d) : formatDivergenceLine2(d));
|
|
5090
|
+
emitted++;
|
|
5091
|
+
}
|
|
5092
|
+
return emitted;
|
|
5093
|
+
}
|
|
5094
|
+
// Emit a stale-transition once, keyed on the edge id.
|
|
5095
|
+
emitStale(edgeId) {
|
|
5096
|
+
const key = `stale|${edgeId}`;
|
|
5097
|
+
if (this.seen.has(key)) return false;
|
|
5098
|
+
this.seen.add(key);
|
|
5099
|
+
this.out(this.opts.json ? staleJson(edgeId) : formatStaleLine(edgeId));
|
|
5100
|
+
return true;
|
|
5101
|
+
}
|
|
5102
|
+
// Emit a new OBSERVED runtime dependency once, keyed on the edge id. Silently
|
|
5103
|
+
// ignores non-OBSERVED edges and non-dependency edge types (structural
|
|
5104
|
+
// ownership), so only real runtime dependencies reach stdout.
|
|
5105
|
+
emitObservedEdge(edge) {
|
|
5106
|
+
if (edge.provenance !== Provenance3.OBSERVED) return false;
|
|
5107
|
+
if (!OBSERVED_DEP_EDGE_TYPES.has(edge.type)) return false;
|
|
5108
|
+
const key = `edge|${edge.id}`;
|
|
5109
|
+
if (this.seen.has(key)) return false;
|
|
5110
|
+
this.seen.add(key);
|
|
5111
|
+
this.out(this.opts.json ? observedEdgeJson(edge) : formatObservedEdgeLine(edge));
|
|
5112
|
+
return true;
|
|
5113
|
+
}
|
|
5114
|
+
};
|
|
5115
|
+
function parseFrame(raw) {
|
|
5116
|
+
let event = "message";
|
|
5117
|
+
const dataLines = [];
|
|
5118
|
+
for (const line of raw.split("\n")) {
|
|
5119
|
+
if (line.length === 0 || line.startsWith(":")) continue;
|
|
5120
|
+
if (line.startsWith("event:")) {
|
|
5121
|
+
event = line.slice("event:".length).trim();
|
|
5122
|
+
} else if (line.startsWith("data:")) {
|
|
5123
|
+
dataLines.push(line.slice("data:".length).replace(/^ /, ""));
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
5126
|
+
if (dataLines.length === 0) return null;
|
|
5127
|
+
return { event, data: dataLines.join("\n") };
|
|
5128
|
+
}
|
|
5129
|
+
async function drainSse(body, onFrame) {
|
|
5130
|
+
const reader = body.getReader();
|
|
5131
|
+
const decoder = new TextDecoder();
|
|
5132
|
+
let buf = "";
|
|
5133
|
+
try {
|
|
5134
|
+
for (; ; ) {
|
|
5135
|
+
const { done, value } = await reader.read();
|
|
5136
|
+
if (done) break;
|
|
5137
|
+
buf += decoder.decode(value, { stream: true });
|
|
5138
|
+
buf = buf.replace(/\r\n/g, "\n");
|
|
5139
|
+
let idx;
|
|
5140
|
+
while ((idx = buf.indexOf("\n\n")) !== -1) {
|
|
5141
|
+
const rawFrame = buf.slice(0, idx);
|
|
5142
|
+
buf = buf.slice(idx + 2);
|
|
5143
|
+
const frame = parseFrame(rawFrame);
|
|
5144
|
+
if (frame) onFrame(frame);
|
|
5145
|
+
}
|
|
5146
|
+
}
|
|
5147
|
+
} finally {
|
|
5148
|
+
try {
|
|
5149
|
+
reader.releaseLock();
|
|
5150
|
+
} catch {
|
|
5151
|
+
}
|
|
5152
|
+
}
|
|
5153
|
+
}
|
|
5154
|
+
function safeParse(data) {
|
|
5155
|
+
try {
|
|
5156
|
+
return JSON.parse(data);
|
|
5157
|
+
} catch {
|
|
5158
|
+
return null;
|
|
5159
|
+
}
|
|
5160
|
+
}
|
|
5161
|
+
function projectPath2(project, suffix) {
|
|
5162
|
+
if (!project) return suffix;
|
|
5163
|
+
return `/projects/${encodeURIComponent(project)}${suffix}`;
|
|
5164
|
+
}
|
|
5165
|
+
function backoffDelay(attempt, capMs) {
|
|
5166
|
+
return Math.min(capMs, 500 * 2 ** Math.min(attempt, 6));
|
|
5167
|
+
}
|
|
5168
|
+
async function runMonitor(opts) {
|
|
5169
|
+
const write = opts.write ?? ((line) => process.stdout.write(line));
|
|
5170
|
+
const debounceMs = opts.debounceMs ?? 400;
|
|
5171
|
+
const backoffCapMs = opts.backoffCapMs ?? 1e4;
|
|
5172
|
+
const maxReconnects = opts.maxReconnects ?? Number.POSITIVE_INFINITY;
|
|
5173
|
+
const client = createHttpClient(opts.baseUrl, opts.authToken);
|
|
5174
|
+
const emitter = new MonitorEmitter({ json: opts.json, write });
|
|
5175
|
+
const divergencesPath = projectPath2(opts.project, "/graph/divergences");
|
|
5176
|
+
const eventsUrl = `${opts.baseUrl.replace(/\/$/, "")}${projectPath2(opts.project, "/events")}`;
|
|
5177
|
+
let readTimer = null;
|
|
5178
|
+
let reading = false;
|
|
5179
|
+
let readPending = false;
|
|
5180
|
+
const doRead = async () => {
|
|
5181
|
+
if (reading) {
|
|
5182
|
+
readPending = true;
|
|
5183
|
+
return;
|
|
5184
|
+
}
|
|
5185
|
+
reading = true;
|
|
5186
|
+
try {
|
|
5187
|
+
const result = await client.get(divergencesPath);
|
|
5188
|
+
emitter.emitDivergences(result);
|
|
5189
|
+
} catch {
|
|
5190
|
+
} finally {
|
|
5191
|
+
reading = false;
|
|
5192
|
+
if (readPending) {
|
|
5193
|
+
readPending = false;
|
|
5194
|
+
scheduleRead();
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
};
|
|
5198
|
+
const scheduleRead = () => {
|
|
5199
|
+
if (readTimer) clearTimeout(readTimer);
|
|
5200
|
+
readTimer = setTimeout(() => {
|
|
5201
|
+
readTimer = null;
|
|
5202
|
+
void doRead();
|
|
5203
|
+
}, debounceMs);
|
|
5204
|
+
if (typeof readTimer.unref === "function") readTimer.unref();
|
|
5205
|
+
};
|
|
5206
|
+
const onFrame = (frame) => {
|
|
5207
|
+
switch (frame.event) {
|
|
5208
|
+
case "extraction-complete":
|
|
5209
|
+
scheduleRead();
|
|
5210
|
+
break;
|
|
5211
|
+
case "stale-transition": {
|
|
5212
|
+
const payload = safeParse(frame.data);
|
|
5213
|
+
const edgeId = payload && typeof payload.edgeId === "string" ? payload.edgeId : void 0;
|
|
5214
|
+
if (edgeId) emitter.emitStale(edgeId);
|
|
5215
|
+
scheduleRead();
|
|
5216
|
+
break;
|
|
5217
|
+
}
|
|
5218
|
+
case "edge-added": {
|
|
5219
|
+
const payload = safeParse(frame.data);
|
|
5220
|
+
const edge = payload?.edge;
|
|
5221
|
+
if (edge && edge.provenance === Provenance3.OBSERVED) {
|
|
5222
|
+
emitter.emitObservedEdge(edge);
|
|
5223
|
+
scheduleRead();
|
|
5224
|
+
}
|
|
5225
|
+
break;
|
|
5226
|
+
}
|
|
5227
|
+
default:
|
|
5228
|
+
break;
|
|
5229
|
+
}
|
|
5230
|
+
};
|
|
5231
|
+
const headers = { accept: "text/event-stream" };
|
|
5232
|
+
if (opts.authToken && opts.authToken.length > 0) {
|
|
5233
|
+
headers.authorization = `Bearer ${opts.authToken}`;
|
|
5234
|
+
}
|
|
5235
|
+
let connectedOnce = false;
|
|
5236
|
+
let firstConnect = true;
|
|
5237
|
+
for (let attempt = 0; ; attempt++) {
|
|
5238
|
+
if (opts.signal?.aborted) break;
|
|
5239
|
+
let res;
|
|
5240
|
+
try {
|
|
5241
|
+
res = await fetch(eventsUrl, { headers, signal: opts.signal });
|
|
5242
|
+
} catch (err) {
|
|
5243
|
+
if (err.name === "AbortError") break;
|
|
5244
|
+
if (!connectedOnce || attempt >= maxReconnects) break;
|
|
5245
|
+
await sleep(backoffDelay(attempt, backoffCapMs), opts.signal);
|
|
5246
|
+
continue;
|
|
5247
|
+
}
|
|
5248
|
+
if (!res.ok || !res.body) {
|
|
5249
|
+
await res.body?.cancel().catch(() => {
|
|
5250
|
+
});
|
|
5251
|
+
if (!connectedOnce || attempt >= maxReconnects) break;
|
|
5252
|
+
await sleep(backoffDelay(attempt, backoffCapMs), opts.signal);
|
|
5253
|
+
continue;
|
|
5254
|
+
}
|
|
5255
|
+
connectedOnce = true;
|
|
5256
|
+
attempt = 0;
|
|
5257
|
+
if (firstConnect) {
|
|
5258
|
+
firstConnect = false;
|
|
5259
|
+
await doRead();
|
|
5260
|
+
} else {
|
|
5261
|
+
scheduleRead();
|
|
5262
|
+
}
|
|
5263
|
+
try {
|
|
5264
|
+
await drainSse(res.body, onFrame);
|
|
5265
|
+
} catch {
|
|
5266
|
+
}
|
|
5267
|
+
if (opts.signal?.aborted) break;
|
|
5268
|
+
if (attempt >= maxReconnects) break;
|
|
5269
|
+
await sleep(backoffDelay(attempt, backoffCapMs), opts.signal);
|
|
5270
|
+
}
|
|
5271
|
+
if (readTimer) clearTimeout(readTimer);
|
|
5272
|
+
return 0;
|
|
5273
|
+
}
|
|
5274
|
+
function sleep(ms, signal) {
|
|
5275
|
+
return new Promise((resolve) => {
|
|
5276
|
+
if (signal?.aborted) {
|
|
5277
|
+
resolve();
|
|
5278
|
+
return;
|
|
5279
|
+
}
|
|
5280
|
+
const timer = setTimeout(() => {
|
|
5281
|
+
signal?.removeEventListener("abort", onAbort);
|
|
5282
|
+
resolve();
|
|
5283
|
+
}, ms);
|
|
5284
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
5285
|
+
const onAbort = () => {
|
|
5286
|
+
clearTimeout(timer);
|
|
5287
|
+
resolve();
|
|
5288
|
+
};
|
|
5289
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
5290
|
+
});
|
|
5291
|
+
}
|
|
5292
|
+
|
|
4973
5293
|
// src/cli-verbs.ts
|
|
5294
|
+
import path10 from "path";
|
|
4974
5295
|
async function resolveProjectEntry(opts) {
|
|
4975
5296
|
const entries = await listProjects();
|
|
4976
5297
|
if (opts.project) {
|
|
@@ -5164,6 +5485,14 @@ function usage2() {
|
|
|
5164
5485
|
console.log(" watch <path> Start neat-core, watch <path>, re-extract on changes.");
|
|
5165
5486
|
console.log(" PORT (default 8080), OTEL_PORT (4318), HOST (0.0.0.0)");
|
|
5166
5487
|
console.log(" control listeners. NEAT_OTLP_GRPC=true also opens 4317.");
|
|
5488
|
+
console.log(" monitor Stream live graph facts to stdout \u2014 one line per new");
|
|
5489
|
+
console.log(" fact \u2014 as the daemon learns them: fresh divergences,");
|
|
5490
|
+
console.log(" integrations that just went stale, and new observed");
|
|
5491
|
+
console.log(" runtime dependencies. Silent when nothing is new; exits");
|
|
5492
|
+
console.log(" clean with no output when no daemon is reachable.");
|
|
5493
|
+
console.log(" Flags:");
|
|
5494
|
+
console.log(" --project <name> watch a registered project by name");
|
|
5495
|
+
console.log(" --json emit one JSON object per line");
|
|
5167
5496
|
console.log(" list Report the daemons running on this machine (alias: ps).");
|
|
5168
5497
|
console.log(" Reads ~/.neat/daemons/ and folds in any registered");
|
|
5169
5498
|
console.log(" project no daemon has self-described yet.");
|
|
@@ -5823,14 +6152,14 @@ async function main() {
|
|
|
5823
6152
|
console.error(`neat uninstall: no project named "${name}"`);
|
|
5824
6153
|
process.exit(1);
|
|
5825
6154
|
}
|
|
5826
|
-
const
|
|
6155
|
+
const projectPath3 = daemon?.record.projectPath ?? removed?.path ?? "(unknown path)";
|
|
5827
6156
|
if (daemon) {
|
|
5828
6157
|
if (daemon.live && signalDaemonStop(daemon.record.pid)) {
|
|
5829
6158
|
console.log(`uninstall: ${name} \u2014 stopped daemon pid ${daemon.record.pid}`);
|
|
5830
6159
|
}
|
|
5831
6160
|
await removeDaemonRecord(daemon.source);
|
|
5832
6161
|
}
|
|
5833
|
-
console.log(`unregistered: ${name} (${
|
|
6162
|
+
console.log(`unregistered: ${name} (${projectPath3})`);
|
|
5834
6163
|
console.log("note: neat-out/, policy.json, and other files at the project path were left in place.");
|
|
5835
6164
|
return;
|
|
5836
6165
|
}
|
|
@@ -5885,6 +6214,11 @@ async function main() {
|
|
|
5885
6214
|
if (result.exitCode !== 0) process.exit(result.exitCode);
|
|
5886
6215
|
return;
|
|
5887
6216
|
}
|
|
6217
|
+
if (cmd === "monitor") {
|
|
6218
|
+
const code = await runMonitorVerb(parsed);
|
|
6219
|
+
if (code !== 0) process.exit(code);
|
|
6220
|
+
return;
|
|
6221
|
+
}
|
|
5888
6222
|
if (QUERY_VERBS.has(cmd)) {
|
|
5889
6223
|
const code = await runQueryVerb(cmd, parsed);
|
|
5890
6224
|
if (code !== 0) process.exit(code);
|
|
@@ -6138,6 +6472,37 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
6138
6472
|
return exitCodeForError(err);
|
|
6139
6473
|
}
|
|
6140
6474
|
}
|
|
6475
|
+
async function runMonitorVerb(parsed) {
|
|
6476
|
+
const requestedProject = resolveProjectFlag(parsed);
|
|
6477
|
+
const baseUrl = await resolveDaemonUrl(requestedProject);
|
|
6478
|
+
const token = resolveAuthToken();
|
|
6479
|
+
const client = createHttpClient(baseUrl, token);
|
|
6480
|
+
let project;
|
|
6481
|
+
try {
|
|
6482
|
+
project = await resolveProjectForVerb(client, parsed);
|
|
6483
|
+
} catch (err) {
|
|
6484
|
+
if (err instanceof ProjectResolutionError) {
|
|
6485
|
+
console.error(`neat monitor: ${err.message}`);
|
|
6486
|
+
}
|
|
6487
|
+
return 0;
|
|
6488
|
+
}
|
|
6489
|
+
const controller = new AbortController();
|
|
6490
|
+
const onSignal = () => controller.abort();
|
|
6491
|
+
process.once("SIGINT", onSignal);
|
|
6492
|
+
process.once("SIGTERM", onSignal);
|
|
6493
|
+
try {
|
|
6494
|
+
return await runMonitor({
|
|
6495
|
+
baseUrl,
|
|
6496
|
+
project,
|
|
6497
|
+
json: parsed.json,
|
|
6498
|
+
authToken: token,
|
|
6499
|
+
signal: controller.signal
|
|
6500
|
+
});
|
|
6501
|
+
} finally {
|
|
6502
|
+
process.off("SIGINT", onSignal);
|
|
6503
|
+
process.off("SIGTERM", onSignal);
|
|
6504
|
+
}
|
|
6505
|
+
}
|
|
6141
6506
|
var entry = process.argv[1] ?? "";
|
|
6142
6507
|
if (/[\\/](?:cli\.(?:cjs|js)|cli|neat|neat\.is)$/.test(entry)) {
|
|
6143
6508
|
main().catch((err) => {
|
|
@@ -6158,6 +6523,7 @@ export {
|
|
|
6158
6523
|
resolveDaemonUrl,
|
|
6159
6524
|
resolveProjectForVerb,
|
|
6160
6525
|
runInit,
|
|
6526
|
+
runMonitorVerb,
|
|
6161
6527
|
runQueryVerb,
|
|
6162
6528
|
runSkill,
|
|
6163
6529
|
usage2 as usage
|