@neat.is/core 0.5.2 → 0.5.4-dev.20260721
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-I72HTUOG.js → chunk-4RU3AAOI.js} +2 -2
- package/dist/{chunk-CFDPIMRP.js → chunk-I4NZ7PSN.js} +3 -2
- package/dist/chunk-I4NZ7PSN.js.map +1 -0
- package/dist/{chunk-BI3XKGVG.js → chunk-PEFX3DBR.js} +473 -127
- package/dist/chunk-PEFX3DBR.js.map +1 -0
- package/dist/{chunk-DPEPI2N6.js → chunk-VR73QNJD.js} +293 -22
- package/dist/chunk-VR73QNJD.js.map +1 -0
- package/dist/cli.cjs +1371 -479
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +343 -75
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +879 -310
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +888 -319
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-IDMIH6ZY.js → otel-grpc-TEGOXE6T.js} +3 -3
- package/dist/server.cjs +553 -192
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-BI3XKGVG.js.map +0 -1
- package/dist/chunk-CFDPIMRP.js.map +0 -1
- package/dist/chunk-DPEPI2N6.js.map +0 -1
- /package/dist/{chunk-I72HTUOG.js.map → chunk-4RU3AAOI.js.map} +0 -0
- /package/dist/{otel-grpc-IDMIH6ZY.js.map → otel-grpc-TEGOXE6T.js.map} +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
|
|
|
61
61
|
]);
|
|
62
62
|
const publicRead = opts.publicRead === true;
|
|
63
63
|
app.addHook("preHandler", (req, reply, done) => {
|
|
64
|
-
const
|
|
65
|
-
if (exactUnauthPaths.has(
|
|
64
|
+
const path63 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
65
|
+
if (exactUnauthPaths.has(path63) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path63)) {
|
|
66
66
|
done();
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req) {
|
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
function resolveProtoRoot() {
|
|
197
|
-
const here =
|
|
198
|
-
return
|
|
197
|
+
const here = import_node_path50.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
198
|
+
return import_node_path50.default.resolve(here, "..", "proto");
|
|
199
199
|
}
|
|
200
200
|
function loadTraceService() {
|
|
201
201
|
const protoRoot = resolveProtoRoot();
|
|
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
263
263
|
})
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
|
-
var import_node_url2,
|
|
266
|
+
var import_node_url2, import_node_path50, import_node_crypto2, grpc, protoLoader;
|
|
267
267
|
var init_otel_grpc = __esm({
|
|
268
268
|
"src/otel-grpc.ts"() {
|
|
269
269
|
"use strict";
|
|
270
270
|
init_cjs_shims();
|
|
271
271
|
import_node_url2 = require("url");
|
|
272
|
-
|
|
272
|
+
import_node_path50 = __toESM(require("path"), 1);
|
|
273
273
|
import_node_crypto2 = require("crypto");
|
|
274
274
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
275
275
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -362,8 +362,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
362
362
|
const v = attrs[key];
|
|
363
363
|
if (typeof v === "string" && v.length > 0) {
|
|
364
364
|
const q = v.indexOf("?");
|
|
365
|
-
const
|
|
366
|
-
if (
|
|
365
|
+
const path63 = q === -1 ? v : v.slice(0, q);
|
|
366
|
+
if (path63.length > 0) return path63;
|
|
367
367
|
}
|
|
368
368
|
}
|
|
369
369
|
return void 0;
|
|
@@ -398,6 +398,7 @@ function parseOtlpRequest(body) {
|
|
|
398
398
|
attributes: attrs,
|
|
399
399
|
dbSystem: typeof attrs["db.system"] === "string" ? attrs["db.system"] : void 0,
|
|
400
400
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
401
|
+
dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
|
|
401
402
|
messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
|
|
402
403
|
messagingDestination: messagingDestinationOf(attrs),
|
|
403
404
|
graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
|
|
@@ -417,10 +418,10 @@ function parseOtlpRequest(body) {
|
|
|
417
418
|
return out;
|
|
418
419
|
}
|
|
419
420
|
function loadProtoRoot() {
|
|
420
|
-
const here =
|
|
421
|
-
const protoRoot =
|
|
421
|
+
const here = import_node_path51.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
|
|
422
|
+
const protoRoot = import_node_path51.default.resolve(here, "..", "proto");
|
|
422
423
|
const root = new import_protobufjs.default.Root();
|
|
423
|
-
root.resolvePath = (_origin, target) =>
|
|
424
|
+
root.resolvePath = (_origin, target) => import_node_path51.default.resolve(protoRoot, target);
|
|
424
425
|
root.loadSync(
|
|
425
426
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
426
427
|
{ keepCase: true }
|
|
@@ -648,12 +649,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
|
|
|
648
649
|
}
|
|
649
650
|
}
|
|
650
651
|
}
|
|
651
|
-
var
|
|
652
|
+
var import_node_path51, import_node_url3, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
|
|
652
653
|
var init_otel = __esm({
|
|
653
654
|
"src/otel.ts"() {
|
|
654
655
|
"use strict";
|
|
655
656
|
init_cjs_shims();
|
|
656
|
-
|
|
657
|
+
import_node_path51 = __toESM(require("path"), 1);
|
|
657
658
|
import_node_url3 = require("url");
|
|
658
659
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
659
660
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -686,12 +687,12 @@ __export(cli_exports, {
|
|
|
686
687
|
runInit: () => runInit,
|
|
687
688
|
runQueryVerb: () => runQueryVerb,
|
|
688
689
|
runSkill: () => runSkill,
|
|
689
|
-
usage: () =>
|
|
690
|
+
usage: () => usage2
|
|
690
691
|
});
|
|
691
692
|
module.exports = __toCommonJS(cli_exports);
|
|
692
693
|
init_cjs_shims();
|
|
693
|
-
var
|
|
694
|
-
var
|
|
694
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
695
|
+
var import_node_fs41 = require("fs");
|
|
695
696
|
|
|
696
697
|
// src/banner.ts
|
|
697
698
|
init_cjs_shims();
|
|
@@ -1256,19 +1257,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1256
1257
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1257
1258
|
let best = { path: [start], edges: [] };
|
|
1258
1259
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1259
|
-
function step(node,
|
|
1260
|
-
if (
|
|
1261
|
-
best = { path: [...
|
|
1260
|
+
function step(node, path63, edges) {
|
|
1261
|
+
if (path63.length > best.path.length) {
|
|
1262
|
+
best = { path: [...path63], edges: [...edges] };
|
|
1262
1263
|
}
|
|
1263
|
-
if (
|
|
1264
|
+
if (path63.length - 1 >= maxDepth) return;
|
|
1264
1265
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1265
1266
|
for (const [srcId, edge] of incoming) {
|
|
1266
1267
|
if (visited.has(srcId)) continue;
|
|
1267
1268
|
visited.add(srcId);
|
|
1268
|
-
|
|
1269
|
+
path63.push(srcId);
|
|
1269
1270
|
edges.push(edge);
|
|
1270
|
-
step(srcId,
|
|
1271
|
-
|
|
1271
|
+
step(srcId, path63, edges);
|
|
1272
|
+
path63.pop();
|
|
1272
1273
|
edges.pop();
|
|
1273
1274
|
visited.delete(srcId);
|
|
1274
1275
|
}
|
|
@@ -1462,26 +1463,26 @@ function dominantFailingCall(graph, serviceId6, visited) {
|
|
|
1462
1463
|
return best;
|
|
1463
1464
|
}
|
|
1464
1465
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1465
|
-
const
|
|
1466
|
+
const path63 = [originServiceId];
|
|
1466
1467
|
const edges = [];
|
|
1467
1468
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1468
1469
|
let current = originServiceId;
|
|
1469
1470
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1470
1471
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1471
1472
|
if (!hop) break;
|
|
1472
|
-
|
|
1473
|
+
path63.push(hop.nextService);
|
|
1473
1474
|
edges.push(hop.edge);
|
|
1474
1475
|
visited.add(hop.nextService);
|
|
1475
1476
|
current = hop.nextService;
|
|
1476
1477
|
}
|
|
1477
1478
|
if (edges.length === 0) return null;
|
|
1478
|
-
return { path:
|
|
1479
|
+
return { path: path63, edges, culprit: current };
|
|
1479
1480
|
}
|
|
1480
1481
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1481
1482
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1482
1483
|
if (!chain) return null;
|
|
1483
1484
|
const culprit = chain.culprit;
|
|
1484
|
-
const
|
|
1485
|
+
const path63 = [...chain.path];
|
|
1485
1486
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1486
1487
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1487
1488
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1489,14 +1490,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1489
1490
|
if (loc) {
|
|
1490
1491
|
let rootCauseNode = culprit;
|
|
1491
1492
|
if (loc.fileNode) {
|
|
1492
|
-
|
|
1493
|
+
path63.push(loc.fileNode);
|
|
1493
1494
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1494
1495
|
rootCauseNode = loc.fileNode;
|
|
1495
1496
|
}
|
|
1496
1497
|
return import_types.RootCauseResultSchema.parse({
|
|
1497
1498
|
rootCauseNode,
|
|
1498
1499
|
rootCauseReason: loc.rootCauseReason,
|
|
1499
|
-
traversalPath:
|
|
1500
|
+
traversalPath: path63,
|
|
1500
1501
|
edgeProvenances,
|
|
1501
1502
|
confidence,
|
|
1502
1503
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1508,7 +1509,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1508
1509
|
return import_types.RootCauseResultSchema.parse({
|
|
1509
1510
|
rootCauseNode: culprit,
|
|
1510
1511
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1511
|
-
traversalPath:
|
|
1512
|
+
traversalPath: path63,
|
|
1512
1513
|
edgeProvenances,
|
|
1513
1514
|
confidence,
|
|
1514
1515
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2585,6 +2586,19 @@ function ensureServiceNode(graph, serviceName, env) {
|
|
|
2585
2586
|
graph.addNode(id, node);
|
|
2586
2587
|
return id;
|
|
2587
2588
|
}
|
|
2589
|
+
function ensureInfraNode(graph, kind, name, provider) {
|
|
2590
|
+
const id = (0, import_types3.infraId)(kind, name);
|
|
2591
|
+
if (graph.hasNode(id)) return id;
|
|
2592
|
+
const node = {
|
|
2593
|
+
id,
|
|
2594
|
+
type: import_types3.NodeType.InfraNode,
|
|
2595
|
+
name,
|
|
2596
|
+
provider,
|
|
2597
|
+
kind
|
|
2598
|
+
};
|
|
2599
|
+
graph.addNode(id, node);
|
|
2600
|
+
return id;
|
|
2601
|
+
}
|
|
2588
2602
|
function ensureDatabaseNode(graph, host, engine) {
|
|
2589
2603
|
const id = (0, import_types3.databaseId)(host);
|
|
2590
2604
|
if (graph.hasNode(id)) return id;
|
|
@@ -2922,6 +2936,18 @@ async function handleSpan(ctx, span) {
|
|
|
2922
2936
|
callSiteEvidence
|
|
2923
2937
|
);
|
|
2924
2938
|
if (result) affectedNode = targetId;
|
|
2939
|
+
if (span.dbSystem === "mongodb" && span.dbCollection) {
|
|
2940
|
+
const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
|
|
2941
|
+
upsertObservedEdge(
|
|
2942
|
+
ctx.graph,
|
|
2943
|
+
import_types3.EdgeType.CALLS,
|
|
2944
|
+
observedSource(),
|
|
2945
|
+
collectionId,
|
|
2946
|
+
ts,
|
|
2947
|
+
isError,
|
|
2948
|
+
callSiteEvidence
|
|
2949
|
+
);
|
|
2950
|
+
}
|
|
2925
2951
|
}
|
|
2926
2952
|
} else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
|
|
2927
2953
|
const targetId = ensureMessagingDestinationNode(
|
|
@@ -4678,14 +4704,21 @@ function engineFromImage(image) {
|
|
|
4678
4704
|
// src/extract/databases/dotenv.ts
|
|
4679
4705
|
var CONNECTION_KEYS = /* @__PURE__ */ new Set([
|
|
4680
4706
|
"DATABASE_URL",
|
|
4707
|
+
"DATABASE_URI",
|
|
4681
4708
|
"DB_URL",
|
|
4709
|
+
"DB_URI",
|
|
4682
4710
|
"POSTGRES_URL",
|
|
4711
|
+
"POSTGRES_URI",
|
|
4683
4712
|
"POSTGRESQL_URL",
|
|
4713
|
+
"POSTGRESQL_URI",
|
|
4684
4714
|
"MYSQL_URL",
|
|
4715
|
+
"MYSQL_URI",
|
|
4716
|
+
"MONGODB_URL",
|
|
4685
4717
|
"MONGODB_URI",
|
|
4686
4718
|
"MONGO_URL",
|
|
4687
4719
|
"MONGO_URI",
|
|
4688
|
-
"REDIS_URL"
|
|
4720
|
+
"REDIS_URL",
|
|
4721
|
+
"REDIS_URI"
|
|
4689
4722
|
]);
|
|
4690
4723
|
function parseDotenvLine(line) {
|
|
4691
4724
|
const trimmed = line.trim();
|
|
@@ -5774,7 +5807,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
5774
5807
|
|
|
5775
5808
|
// src/extract/calls/index.ts
|
|
5776
5809
|
init_cjs_shims();
|
|
5777
|
-
var
|
|
5810
|
+
var import_types21 = require("@neat.is/types");
|
|
5778
5811
|
|
|
5779
5812
|
// src/extract/calls/http.ts
|
|
5780
5813
|
init_cjs_shims();
|
|
@@ -6487,15 +6520,311 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6487
6520
|
return out;
|
|
6488
6521
|
}
|
|
6489
6522
|
|
|
6523
|
+
// src/extract/calls/mongoose.ts
|
|
6524
|
+
init_cjs_shims();
|
|
6525
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
6526
|
+
var import_types20 = require("@neat.is/types");
|
|
6527
|
+
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
6528
|
+
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
6529
|
+
var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
|
|
6530
|
+
var UNCOUNTABLES = /* @__PURE__ */ new Set([
|
|
6531
|
+
"advice",
|
|
6532
|
+
"energy",
|
|
6533
|
+
"excretion",
|
|
6534
|
+
"digestion",
|
|
6535
|
+
"cooperation",
|
|
6536
|
+
"health",
|
|
6537
|
+
"justice",
|
|
6538
|
+
"labour",
|
|
6539
|
+
"machinery",
|
|
6540
|
+
"equipment",
|
|
6541
|
+
"information",
|
|
6542
|
+
"pollution",
|
|
6543
|
+
"sewage",
|
|
6544
|
+
"paper",
|
|
6545
|
+
"money",
|
|
6546
|
+
"species",
|
|
6547
|
+
"series",
|
|
6548
|
+
"rain",
|
|
6549
|
+
"rice",
|
|
6550
|
+
"fish",
|
|
6551
|
+
"sheep",
|
|
6552
|
+
"moose",
|
|
6553
|
+
"deer",
|
|
6554
|
+
"news",
|
|
6555
|
+
"expertise",
|
|
6556
|
+
"status",
|
|
6557
|
+
"media"
|
|
6558
|
+
]);
|
|
6559
|
+
var PLURALIZE_RULES = [
|
|
6560
|
+
[/human$/gi, "humans"],
|
|
6561
|
+
[/(m)an$/gi, "$1en"],
|
|
6562
|
+
[/(pe)rson$/gi, "$1ople"],
|
|
6563
|
+
[/(child)$/gi, "$1ren"],
|
|
6564
|
+
[/^(ox)$/gi, "$1en"],
|
|
6565
|
+
[/(ax|test)is$/gi, "$1es"],
|
|
6566
|
+
[/(octop|vir)us$/gi, "$1i"],
|
|
6567
|
+
[/(alias|status)$/gi, "$1es"],
|
|
6568
|
+
[/(bu)s$/gi, "$1ses"],
|
|
6569
|
+
[/(buffal|tomat|potat)o$/gi, "$1oes"],
|
|
6570
|
+
[/([ti])um$/gi, "$1a"],
|
|
6571
|
+
[/sis$/gi, "ses"],
|
|
6572
|
+
[/(?:([^f])fe|([lr])f)$/gi, "$1$2ves"],
|
|
6573
|
+
[/(hive)$/gi, "$1s"],
|
|
6574
|
+
[/([^aeiouy]|qu)y$/gi, "$1ies"],
|
|
6575
|
+
[/(x|ch|ss|sh)$/gi, "$1es"],
|
|
6576
|
+
[/(matr|vert|ind)ix|ex$/gi, "$1ices"],
|
|
6577
|
+
[/([m|l])ouse$/gi, "$1ice"],
|
|
6578
|
+
[/(kn|w|l)ife$/gi, "$1ives"],
|
|
6579
|
+
[/(quiz)$/gi, "$1zes"],
|
|
6580
|
+
[/s$/gi, "s"],
|
|
6581
|
+
[/([^a-z])$/, "$1"],
|
|
6582
|
+
[/$/gi, "s"]
|
|
6583
|
+
];
|
|
6584
|
+
function pluralizeCollection(name) {
|
|
6585
|
+
const str = name.toLowerCase();
|
|
6586
|
+
if (UNCOUNTABLES.has(str)) return str;
|
|
6587
|
+
for (const [re, repl] of PLURALIZE_RULES) {
|
|
6588
|
+
if (str.match(re)) return str.replace(re, repl);
|
|
6589
|
+
}
|
|
6590
|
+
return str;
|
|
6591
|
+
}
|
|
6592
|
+
var MODEL_METHODS = [
|
|
6593
|
+
"find",
|
|
6594
|
+
"findOne",
|
|
6595
|
+
"findById",
|
|
6596
|
+
"findByIdAndUpdate",
|
|
6597
|
+
"findByIdAndDelete",
|
|
6598
|
+
"findOneAndUpdate",
|
|
6599
|
+
"findOneAndDelete",
|
|
6600
|
+
"findOneAndReplace",
|
|
6601
|
+
"countDocuments",
|
|
6602
|
+
"estimatedDocumentCount",
|
|
6603
|
+
"distinct",
|
|
6604
|
+
"aggregate",
|
|
6605
|
+
"exists",
|
|
6606
|
+
"where",
|
|
6607
|
+
"populate",
|
|
6608
|
+
"watch",
|
|
6609
|
+
"hydrate",
|
|
6610
|
+
"create",
|
|
6611
|
+
"insertMany",
|
|
6612
|
+
"insertOne",
|
|
6613
|
+
"updateOne",
|
|
6614
|
+
"updateMany",
|
|
6615
|
+
"replaceOne",
|
|
6616
|
+
"deleteOne",
|
|
6617
|
+
"deleteMany",
|
|
6618
|
+
"bulkWrite",
|
|
6619
|
+
"bulkSave",
|
|
6620
|
+
"save",
|
|
6621
|
+
// pre-v7, removed in current mongoose but present in older codebases:
|
|
6622
|
+
"count",
|
|
6623
|
+
"update",
|
|
6624
|
+
"remove",
|
|
6625
|
+
"findOneAndRemove",
|
|
6626
|
+
"findByIdAndRemove"
|
|
6627
|
+
];
|
|
6628
|
+
var MODEL_METHODS_ALT = MODEL_METHODS.join("|");
|
|
6629
|
+
function schemaCollectionVars(content) {
|
|
6630
|
+
const out = /* @__PURE__ */ new Map();
|
|
6631
|
+
const re = /(?:const|let|var)\s+(\w+)\s*=\s*new\s+(?:mongoose\s*\.\s*)?Schema\s*\(/g;
|
|
6632
|
+
let m;
|
|
6633
|
+
while ((m = re.exec(content)) !== null) {
|
|
6634
|
+
const windowText = content.slice(m.index, m.index + 2e3);
|
|
6635
|
+
const opt = /\bcollection\s*:\s*['"`]([\w.-]+)['"`]/.exec(windowText);
|
|
6636
|
+
if (opt) out.set(m[1], opt[1]);
|
|
6637
|
+
}
|
|
6638
|
+
return out;
|
|
6639
|
+
}
|
|
6640
|
+
function resolveModel(modelName, rest, schemaColl, pluralizeOn) {
|
|
6641
|
+
const trimmed = rest.trim();
|
|
6642
|
+
const literalThird = /,\s*['"`]([\w.-]+)['"`]\s*$/.exec(trimmed);
|
|
6643
|
+
if (literalThird) return { name: literalThird[1], kind: "mongodb-collection" };
|
|
6644
|
+
const firstIdent = /^([A-Za-z_$][\w$]*)/.exec(trimmed)?.[1];
|
|
6645
|
+
if (firstIdent && schemaColl.has(firstIdent)) {
|
|
6646
|
+
return { name: schemaColl.get(firstIdent), kind: "mongodb-collection" };
|
|
6647
|
+
}
|
|
6648
|
+
if (/,\s*[A-Za-z_$][\w$]*\s*$/.test(trimmed)) {
|
|
6649
|
+
return { name: modelName, kind: "mongodb-model" };
|
|
6650
|
+
}
|
|
6651
|
+
return { name: pluralizeOn ? pluralizeCollection(modelName) : modelName, kind: "mongodb-collection" };
|
|
6652
|
+
}
|
|
6653
|
+
function collectModelDefs(content, pluralizeOn) {
|
|
6654
|
+
const schemaColl = schemaCollectionVars(content);
|
|
6655
|
+
const out = [];
|
|
6656
|
+
const re = /(?:(?:const|let|var)\s+(\w+)\s*=\s*)?(?:await\s+)?(?:\b\w+\s*\.\s*)?\bmodel\s*\(\s*['"`]([\w$]+)['"`]\s*(?:,\s*([\s\S]{0,300}?))?\)/g;
|
|
6657
|
+
let m;
|
|
6658
|
+
while ((m = re.exec(content)) !== null) {
|
|
6659
|
+
out.push({
|
|
6660
|
+
modelName: m[2],
|
|
6661
|
+
varName: m[1] ?? null,
|
|
6662
|
+
resolved: resolveModel(m[2], m[3] ?? "", schemaColl, pluralizeOn),
|
|
6663
|
+
matchText: m[0]
|
|
6664
|
+
});
|
|
6665
|
+
}
|
|
6666
|
+
return out;
|
|
6667
|
+
}
|
|
6668
|
+
function endpoint(r, file, serviceDir, matchText) {
|
|
6669
|
+
const line = lineOf(file.content, matchText);
|
|
6670
|
+
return {
|
|
6671
|
+
infraId: (0, import_types20.infraId)(r.kind, r.name),
|
|
6672
|
+
name: r.name,
|
|
6673
|
+
kind: r.kind,
|
|
6674
|
+
edgeType: "CALLS",
|
|
6675
|
+
confidenceKind: "verified-call-site",
|
|
6676
|
+
evidence: { file: import_node_path32.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
6677
|
+
};
|
|
6678
|
+
}
|
|
6679
|
+
function mongooseEndpointsFromFile(file, serviceDir) {
|
|
6680
|
+
const hasMongoose = MONGOOSE_IMPORT_RE.test(file.content);
|
|
6681
|
+
const hasMongodb = MONGODB_IMPORT_RE.test(file.content);
|
|
6682
|
+
if (!hasMongoose && !hasMongodb) return [];
|
|
6683
|
+
const content = file.content;
|
|
6684
|
+
const out = [];
|
|
6685
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6686
|
+
const push = (r, matchText) => {
|
|
6687
|
+
const key = `${r.kind}/${r.name}`;
|
|
6688
|
+
if (seen.has(key)) return;
|
|
6689
|
+
seen.add(key);
|
|
6690
|
+
out.push(endpoint(r, file, serviceDir, matchText));
|
|
6691
|
+
};
|
|
6692
|
+
if (hasMongoose) {
|
|
6693
|
+
const pluralizeOn = !PLURALIZE_DISABLED_RE.test(content);
|
|
6694
|
+
for (const def of collectModelDefs(content, pluralizeOn)) push(def.resolved, def.matchText);
|
|
6695
|
+
}
|
|
6696
|
+
const collRe = /\.\s*collection\s*\(\s*['"`]([\w.$-]+)['"`]\s*\)/g;
|
|
6697
|
+
let c;
|
|
6698
|
+
while ((c = collRe.exec(content)) !== null) {
|
|
6699
|
+
push({ name: c[1], kind: "mongodb-collection" }, c[0]);
|
|
6700
|
+
}
|
|
6701
|
+
return out;
|
|
6702
|
+
}
|
|
6703
|
+
function parseDestructure(inner, specifier, out) {
|
|
6704
|
+
for (const raw of inner.split(",")) {
|
|
6705
|
+
const part = raw.trim();
|
|
6706
|
+
if (!part) continue;
|
|
6707
|
+
const asM = /^(\w+)\s+as\s+(\w+)$/.exec(part);
|
|
6708
|
+
const colonM = /^(\w+)\s*:\s*(\w+)$/.exec(part);
|
|
6709
|
+
if (asM) out.push({ local: asM[2], kind: "named", exportName: asM[1], specifier });
|
|
6710
|
+
else if (colonM) out.push({ local: colonM[2], kind: "named", exportName: colonM[1], specifier });
|
|
6711
|
+
else {
|
|
6712
|
+
const id = /^(\w+)$/.exec(part);
|
|
6713
|
+
if (id) out.push({ local: id[1], kind: "named", exportName: id[1], specifier });
|
|
6714
|
+
}
|
|
6715
|
+
}
|
|
6716
|
+
}
|
|
6717
|
+
function parseImportBindings(content) {
|
|
6718
|
+
const out = [];
|
|
6719
|
+
const esm = /import\s+([^;'"`\n]+?)\s+from\s*['"`]([^'"`]+)['"`]/g;
|
|
6720
|
+
let m;
|
|
6721
|
+
while ((m = esm.exec(content)) !== null) {
|
|
6722
|
+
const clause = m[1].trim();
|
|
6723
|
+
const spec = m[2];
|
|
6724
|
+
const ns = /^\*\s+as\s+(\w+)$/.exec(clause);
|
|
6725
|
+
if (ns) {
|
|
6726
|
+
out.push({ local: ns[1], kind: "namespace", specifier: spec });
|
|
6727
|
+
continue;
|
|
6728
|
+
}
|
|
6729
|
+
const named = /\{([^}]*)\}/.exec(clause);
|
|
6730
|
+
if (named) parseDestructure(named[1], spec, out);
|
|
6731
|
+
const def = /^(\w+)\s*(?:,|$)/.exec(clause);
|
|
6732
|
+
if (def && !clause.startsWith("{")) out.push({ local: def[1], kind: "default", specifier: spec });
|
|
6733
|
+
}
|
|
6734
|
+
const cjs = /(?:const|let|var)\s+(\{[^}]*\}|\w+)\s*=\s*require\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
6735
|
+
while ((m = cjs.exec(content)) !== null) {
|
|
6736
|
+
const lhs = m[1];
|
|
6737
|
+
const spec = m[2];
|
|
6738
|
+
if (lhs.startsWith("{")) parseDestructure(lhs.slice(1, -1), spec, out);
|
|
6739
|
+
else out.push({ local: lhs, kind: "whole", specifier: spec });
|
|
6740
|
+
}
|
|
6741
|
+
return out;
|
|
6742
|
+
}
|
|
6743
|
+
function fileExportsOf(content, pluralizeOn) {
|
|
6744
|
+
const defs = collectModelDefs(content, pluralizeOn).filter((d) => d.resolved.kind === "mongodb-collection");
|
|
6745
|
+
if (defs.length === 0) return null;
|
|
6746
|
+
const byVar = /* @__PURE__ */ new Map();
|
|
6747
|
+
for (const d of defs) if (d.varName) byVar.set(d.varName, d.resolved.name);
|
|
6748
|
+
const byName = new Map(byVar);
|
|
6749
|
+
let def;
|
|
6750
|
+
const named = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
|
|
6751
|
+
if (named && byVar.has(named[1])) def = byVar.get(named[1]);
|
|
6752
|
+
if (!def) {
|
|
6753
|
+
const inline = /(?:module\.exports|export\s+default)\s*=\s*(?:await\s+)?(?:\w+\s*\.\s*)?model\s*\(\s*['"`]([\w$]+)['"`]/.exec(content);
|
|
6754
|
+
if (inline) def = pluralizeOn ? pluralizeCollection(inline[1]) : inline[1];
|
|
6755
|
+
}
|
|
6756
|
+
if (!def && byVar.size === 1) def = [...byVar.values()][0];
|
|
6757
|
+
return { byName, ...def ? { default: def } : {} };
|
|
6758
|
+
}
|
|
6759
|
+
async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
6760
|
+
const mongooseFiles = files.filter((f) => MONGOOSE_IMPORT_RE.test(f.content));
|
|
6761
|
+
if (mongooseFiles.length === 0) return [];
|
|
6762
|
+
const pluralizeOn = !files.some((f) => PLURALIZE_DISABLED_RE.test(f.content));
|
|
6763
|
+
const registry = /* @__PURE__ */ new Map();
|
|
6764
|
+
for (const f of mongooseFiles) {
|
|
6765
|
+
const fx = fileExportsOf(f.content, pluralizeOn);
|
|
6766
|
+
if (fx) registry.set(toPosix2(import_node_path32.default.relative(serviceDir, f.path)), fx);
|
|
6767
|
+
}
|
|
6768
|
+
if (registry.size === 0) return [];
|
|
6769
|
+
const out = [];
|
|
6770
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6771
|
+
for (const f of files) {
|
|
6772
|
+
const bindings = parseImportBindings(f.content);
|
|
6773
|
+
if (bindings.length === 0) continue;
|
|
6774
|
+
const directColl = /* @__PURE__ */ new Map();
|
|
6775
|
+
const nsExports = /* @__PURE__ */ new Map();
|
|
6776
|
+
for (const b of bindings) {
|
|
6777
|
+
const resolvedRel = await resolveJsImport(b.specifier, import_node_path32.default.dirname(f.path), serviceDir, null);
|
|
6778
|
+
if (!resolvedRel) continue;
|
|
6779
|
+
const fx = registry.get(resolvedRel);
|
|
6780
|
+
if (!fx) continue;
|
|
6781
|
+
if (b.kind === "named" && b.exportName) {
|
|
6782
|
+
const coll = fx.byName.get(b.exportName);
|
|
6783
|
+
if (coll) directColl.set(b.local, coll);
|
|
6784
|
+
} else if (b.kind === "default") {
|
|
6785
|
+
if (fx.default) directColl.set(b.local, fx.default);
|
|
6786
|
+
} else {
|
|
6787
|
+
if (fx.default) directColl.set(b.local, fx.default);
|
|
6788
|
+
nsExports.set(b.local, fx);
|
|
6789
|
+
}
|
|
6790
|
+
}
|
|
6791
|
+
if (directColl.size === 0 && nsExports.size === 0) continue;
|
|
6792
|
+
const localDefs = new Set(
|
|
6793
|
+
collectModelDefs(f.content, pluralizeOn).map((d) => d.varName).filter((v) => v !== null)
|
|
6794
|
+
);
|
|
6795
|
+
const emit = (collection, matchText) => {
|
|
6796
|
+
const key = `${f.path}::${collection}`;
|
|
6797
|
+
if (seen.has(key)) return;
|
|
6798
|
+
seen.add(key);
|
|
6799
|
+
out.push(endpoint({ name: collection, kind: "mongodb-collection" }, f, serviceDir, matchText));
|
|
6800
|
+
};
|
|
6801
|
+
for (const [local, collection] of directColl) {
|
|
6802
|
+
if (localDefs.has(local)) continue;
|
|
6803
|
+
const qre = new RegExp(`\\b${local}\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
|
|
6804
|
+
let qm;
|
|
6805
|
+
while ((qm = qre.exec(f.content)) !== null) emit(collection, qm[0]);
|
|
6806
|
+
}
|
|
6807
|
+
for (const [local, fx] of nsExports) {
|
|
6808
|
+
const qre = new RegExp(`\\b${local}\\s*\\.\\s*(\\w+)\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
|
|
6809
|
+
let qm;
|
|
6810
|
+
while ((qm = qre.exec(f.content)) !== null) {
|
|
6811
|
+
const coll = fx.byName.get(qm[1]);
|
|
6812
|
+
if (coll) emit(coll, qm[0]);
|
|
6813
|
+
}
|
|
6814
|
+
}
|
|
6815
|
+
}
|
|
6816
|
+
return out;
|
|
6817
|
+
}
|
|
6818
|
+
|
|
6490
6819
|
// src/extract/calls/index.ts
|
|
6491
6820
|
function edgeTypeFromEndpoint(ep) {
|
|
6492
6821
|
switch (ep.edgeType) {
|
|
6493
6822
|
case "PUBLISHES_TO":
|
|
6494
|
-
return
|
|
6823
|
+
return import_types21.EdgeType.PUBLISHES_TO;
|
|
6495
6824
|
case "CONSUMES_FROM":
|
|
6496
|
-
return
|
|
6825
|
+
return import_types21.EdgeType.CONSUMES_FROM;
|
|
6497
6826
|
default:
|
|
6498
|
-
return
|
|
6827
|
+
return import_types21.EdgeType.CALLS;
|
|
6499
6828
|
}
|
|
6500
6829
|
}
|
|
6501
6830
|
function isAwsKind(kind) {
|
|
@@ -6507,23 +6836,27 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6507
6836
|
for (const service of services) {
|
|
6508
6837
|
const files = await loadSourceFiles(service.dir);
|
|
6509
6838
|
const endpoints = [];
|
|
6839
|
+
const maskedFiles = [];
|
|
6510
6840
|
for (const file of files) {
|
|
6511
6841
|
if (isTestPath(file.path)) continue;
|
|
6512
6842
|
const masked = maskCommentsInSource(file.content);
|
|
6513
6843
|
const maskedFile = { path: file.path, content: masked };
|
|
6844
|
+
maskedFiles.push(maskedFile);
|
|
6514
6845
|
endpoints.push(...kafkaEndpointsFromFile(maskedFile, service.dir));
|
|
6515
6846
|
endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
|
|
6516
6847
|
endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
|
|
6517
6848
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
6518
6849
|
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
6850
|
+
endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
|
|
6519
6851
|
}
|
|
6852
|
+
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
6520
6853
|
if (endpoints.length === 0) continue;
|
|
6521
6854
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
6522
6855
|
for (const ep of endpoints) {
|
|
6523
6856
|
if (!graph.hasNode(ep.infraId)) {
|
|
6524
6857
|
const node = {
|
|
6525
6858
|
id: ep.infraId,
|
|
6526
|
-
type:
|
|
6859
|
+
type: import_types21.NodeType.InfraNode,
|
|
6527
6860
|
name: ep.name,
|
|
6528
6861
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
6529
6862
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -6535,7 +6868,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6535
6868
|
nodesAdded++;
|
|
6536
6869
|
}
|
|
6537
6870
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
6538
|
-
const confidence = (0,
|
|
6871
|
+
const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
|
|
6539
6872
|
const relFile = toPosix2(ep.evidence.file);
|
|
6540
6873
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
6541
6874
|
graph,
|
|
@@ -6545,7 +6878,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6545
6878
|
);
|
|
6546
6879
|
nodesAdded += n;
|
|
6547
6880
|
edgesAdded += e;
|
|
6548
|
-
if (!(0,
|
|
6881
|
+
if (!(0, import_types21.passesExtractedFloor)(confidence)) {
|
|
6549
6882
|
noteExtractedDropped({
|
|
6550
6883
|
source: fileNodeId,
|
|
6551
6884
|
target: ep.infraId,
|
|
@@ -6565,7 +6898,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6565
6898
|
source: fileNodeId,
|
|
6566
6899
|
target: ep.infraId,
|
|
6567
6900
|
type: edgeType,
|
|
6568
|
-
provenance:
|
|
6901
|
+
provenance: import_types21.Provenance.EXTRACTED,
|
|
6569
6902
|
confidence,
|
|
6570
6903
|
evidence: ep.evidence
|
|
6571
6904
|
};
|
|
@@ -6591,16 +6924,16 @@ init_cjs_shims();
|
|
|
6591
6924
|
|
|
6592
6925
|
// src/extract/infra/docker-compose.ts
|
|
6593
6926
|
init_cjs_shims();
|
|
6594
|
-
var
|
|
6595
|
-
var
|
|
6927
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
6928
|
+
var import_types23 = require("@neat.is/types");
|
|
6596
6929
|
|
|
6597
6930
|
// src/extract/infra/shared.ts
|
|
6598
6931
|
init_cjs_shims();
|
|
6599
|
-
var
|
|
6932
|
+
var import_types22 = require("@neat.is/types");
|
|
6600
6933
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
6601
6934
|
return {
|
|
6602
|
-
id: (0,
|
|
6603
|
-
type:
|
|
6935
|
+
id: (0, import_types22.infraId)(kind, name),
|
|
6936
|
+
type: import_types22.NodeType.InfraNode,
|
|
6604
6937
|
name,
|
|
6605
6938
|
provider,
|
|
6606
6939
|
kind,
|
|
@@ -6644,8 +6977,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
6644
6977
|
source: anchorId,
|
|
6645
6978
|
target: node.id,
|
|
6646
6979
|
type: edgeType,
|
|
6647
|
-
provenance:
|
|
6648
|
-
confidence: (0,
|
|
6980
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
6981
|
+
confidence: (0, import_types22.confidenceForExtracted)("structural"),
|
|
6649
6982
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
6650
6983
|
};
|
|
6651
6984
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6662,7 +6995,7 @@ function dependsOnList(value) {
|
|
|
6662
6995
|
}
|
|
6663
6996
|
function serviceNameToServiceNode(name, services) {
|
|
6664
6997
|
for (const s of services) {
|
|
6665
|
-
if (s.node.name === name ||
|
|
6998
|
+
if (s.node.name === name || import_node_path33.default.basename(s.dir) === name) return s.node.id;
|
|
6666
6999
|
}
|
|
6667
7000
|
return null;
|
|
6668
7001
|
}
|
|
@@ -6671,7 +7004,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6671
7004
|
let edgesAdded = 0;
|
|
6672
7005
|
let composePath = null;
|
|
6673
7006
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
6674
|
-
const abs =
|
|
7007
|
+
const abs = import_node_path33.default.join(scanPath, name);
|
|
6675
7008
|
if (await exists(abs)) {
|
|
6676
7009
|
composePath = abs;
|
|
6677
7010
|
break;
|
|
@@ -6684,13 +7017,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6684
7017
|
} catch (err) {
|
|
6685
7018
|
recordExtractionError(
|
|
6686
7019
|
"infra docker-compose",
|
|
6687
|
-
|
|
7020
|
+
import_node_path33.default.relative(scanPath, composePath),
|
|
6688
7021
|
err
|
|
6689
7022
|
);
|
|
6690
7023
|
return { nodesAdded, edgesAdded };
|
|
6691
7024
|
}
|
|
6692
7025
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
6693
|
-
const evidenceFile =
|
|
7026
|
+
const evidenceFile = import_node_path33.default.relative(scanPath, composePath).split(import_node_path33.default.sep).join("/");
|
|
6694
7027
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
6695
7028
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
6696
7029
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -6712,15 +7045,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6712
7045
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
6713
7046
|
const targetId = composeNameToNodeId.get(dep);
|
|
6714
7047
|
if (!targetId) continue;
|
|
6715
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
7048
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types23.EdgeType.DEPENDS_ON);
|
|
6716
7049
|
if (graph.hasEdge(edgeId)) continue;
|
|
6717
7050
|
const edge = {
|
|
6718
7051
|
id: edgeId,
|
|
6719
7052
|
source: sourceId,
|
|
6720
7053
|
target: targetId,
|
|
6721
|
-
type:
|
|
6722
|
-
provenance:
|
|
6723
|
-
confidence: (0,
|
|
7054
|
+
type: import_types23.EdgeType.DEPENDS_ON,
|
|
7055
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
7056
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
6724
7057
|
evidence: { file: evidenceFile }
|
|
6725
7058
|
};
|
|
6726
7059
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6732,9 +7065,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6732
7065
|
|
|
6733
7066
|
// src/extract/infra/dockerfile.ts
|
|
6734
7067
|
init_cjs_shims();
|
|
6735
|
-
var
|
|
7068
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
6736
7069
|
var import_node_fs17 = require("fs");
|
|
6737
|
-
var
|
|
7070
|
+
var import_types24 = require("@neat.is/types");
|
|
6738
7071
|
function readDockerfile(content) {
|
|
6739
7072
|
let image = null;
|
|
6740
7073
|
const ports = [];
|
|
@@ -6763,7 +7096,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6763
7096
|
let nodesAdded = 0;
|
|
6764
7097
|
let edgesAdded = 0;
|
|
6765
7098
|
for (const service of services) {
|
|
6766
|
-
const dockerfilePath =
|
|
7099
|
+
const dockerfilePath = import_node_path34.default.join(service.dir, "Dockerfile");
|
|
6767
7100
|
if (!await exists(dockerfilePath)) continue;
|
|
6768
7101
|
let content;
|
|
6769
7102
|
try {
|
|
@@ -6771,7 +7104,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6771
7104
|
} catch (err) {
|
|
6772
7105
|
recordExtractionError(
|
|
6773
7106
|
"infra dockerfile",
|
|
6774
|
-
|
|
7107
|
+
import_node_path34.default.relative(scanPath, dockerfilePath),
|
|
6775
7108
|
err
|
|
6776
7109
|
);
|
|
6777
7110
|
continue;
|
|
@@ -6783,8 +7116,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6783
7116
|
graph.addNode(node.id, node);
|
|
6784
7117
|
nodesAdded++;
|
|
6785
7118
|
}
|
|
6786
|
-
const relDockerfile = toPosix2(
|
|
6787
|
-
const evidenceFile = toPosix2(
|
|
7119
|
+
const relDockerfile = toPosix2(import_node_path34.default.relative(service.dir, dockerfilePath));
|
|
7120
|
+
const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, dockerfilePath));
|
|
6788
7121
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
6789
7122
|
graph,
|
|
6790
7123
|
service.pkg.name,
|
|
@@ -6793,15 +7126,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6793
7126
|
);
|
|
6794
7127
|
nodesAdded += fn;
|
|
6795
7128
|
edgesAdded += fe;
|
|
6796
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
7129
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types24.EdgeType.RUNS_ON);
|
|
6797
7130
|
if (!graph.hasEdge(edgeId)) {
|
|
6798
7131
|
const edge = {
|
|
6799
7132
|
id: edgeId,
|
|
6800
7133
|
source: fileNodeId,
|
|
6801
7134
|
target: node.id,
|
|
6802
|
-
type:
|
|
6803
|
-
provenance:
|
|
6804
|
-
confidence: (0,
|
|
7135
|
+
type: import_types24.EdgeType.RUNS_ON,
|
|
7136
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7137
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6805
7138
|
evidence: {
|
|
6806
7139
|
file: evidenceFile,
|
|
6807
7140
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -6816,15 +7149,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6816
7149
|
graph.addNode(portNode.id, portNode);
|
|
6817
7150
|
nodesAdded++;
|
|
6818
7151
|
}
|
|
6819
|
-
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id,
|
|
7152
|
+
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types24.EdgeType.CONNECTS_TO);
|
|
6820
7153
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
6821
7154
|
const portEdge = {
|
|
6822
7155
|
id: portEdgeId,
|
|
6823
7156
|
source: fileNodeId,
|
|
6824
7157
|
target: portNode.id,
|
|
6825
|
-
type:
|
|
6826
|
-
provenance:
|
|
6827
|
-
confidence: (0,
|
|
7158
|
+
type: import_types24.EdgeType.CONNECTS_TO,
|
|
7159
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7160
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6828
7161
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
6829
7162
|
};
|
|
6830
7163
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -6837,8 +7170,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6837
7170
|
// src/extract/infra/terraform.ts
|
|
6838
7171
|
init_cjs_shims();
|
|
6839
7172
|
var import_node_fs18 = require("fs");
|
|
6840
|
-
var
|
|
6841
|
-
var
|
|
7173
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
7174
|
+
var import_types25 = require("@neat.is/types");
|
|
6842
7175
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
6843
7176
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
6844
7177
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -6848,11 +7181,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
6848
7181
|
for (const entry2 of entries) {
|
|
6849
7182
|
if (entry2.isDirectory()) {
|
|
6850
7183
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
6851
|
-
const child =
|
|
7184
|
+
const child = import_node_path35.default.join(start, entry2.name);
|
|
6852
7185
|
if (await isPythonVenvDir(child)) continue;
|
|
6853
7186
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
6854
7187
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
6855
|
-
out.push(
|
|
7188
|
+
out.push(import_node_path35.default.join(start, entry2.name));
|
|
6856
7189
|
}
|
|
6857
7190
|
}
|
|
6858
7191
|
return out;
|
|
@@ -6884,7 +7217,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6884
7217
|
const files = await walkTfFiles(scanPath);
|
|
6885
7218
|
for (const file of files) {
|
|
6886
7219
|
const content = await import_node_fs18.promises.readFile(file, "utf8");
|
|
6887
|
-
const evidenceFile = toPosix2(
|
|
7220
|
+
const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, file));
|
|
6888
7221
|
const resources = [];
|
|
6889
7222
|
const byKey = /* @__PURE__ */ new Map();
|
|
6890
7223
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -6919,16 +7252,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6919
7252
|
if (!target) continue;
|
|
6920
7253
|
if (seen.has(target.nodeId)) continue;
|
|
6921
7254
|
seen.add(target.nodeId);
|
|
6922
|
-
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
7255
|
+
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types25.EdgeType.DEPENDS_ON);
|
|
6923
7256
|
if (graph.hasEdge(edgeId)) continue;
|
|
6924
7257
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
6925
7258
|
const edge = {
|
|
6926
7259
|
id: edgeId,
|
|
6927
7260
|
source: resource.nodeId,
|
|
6928
7261
|
target: target.nodeId,
|
|
6929
|
-
type:
|
|
6930
|
-
provenance:
|
|
6931
|
-
confidence: (0,
|
|
7262
|
+
type: import_types25.EdgeType.DEPENDS_ON,
|
|
7263
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7264
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
6932
7265
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
6933
7266
|
};
|
|
6934
7267
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6942,7 +7275,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6942
7275
|
// src/extract/infra/k8s.ts
|
|
6943
7276
|
init_cjs_shims();
|
|
6944
7277
|
var import_node_fs19 = require("fs");
|
|
6945
|
-
var
|
|
7278
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
6946
7279
|
var import_yaml3 = require("yaml");
|
|
6947
7280
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
6948
7281
|
Service: "k8s-service",
|
|
@@ -6960,11 +7293,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
6960
7293
|
for (const entry2 of entries) {
|
|
6961
7294
|
if (entry2.isDirectory()) {
|
|
6962
7295
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
6963
|
-
const child =
|
|
7296
|
+
const child = import_node_path36.default.join(start, entry2.name);
|
|
6964
7297
|
if (await isPythonVenvDir(child)) continue;
|
|
6965
7298
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
6966
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
6967
|
-
out.push(
|
|
7299
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path36.default.extname(entry2.name))) {
|
|
7300
|
+
out.push(import_node_path36.default.join(start, entry2.name));
|
|
6968
7301
|
}
|
|
6969
7302
|
}
|
|
6970
7303
|
return out;
|
|
@@ -6998,13 +7331,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
6998
7331
|
// src/extract/infra/cloudflare.ts
|
|
6999
7332
|
init_cjs_shims();
|
|
7000
7333
|
var import_node_fs20 = require("fs");
|
|
7001
|
-
var
|
|
7334
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
7002
7335
|
var import_smol_toml2 = require("smol-toml");
|
|
7003
|
-
var
|
|
7336
|
+
var import_types26 = require("@neat.is/types");
|
|
7004
7337
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
7005
7338
|
async function readWranglerConfig(dir) {
|
|
7006
7339
|
for (const filename of WRANGLER_FILENAMES) {
|
|
7007
|
-
const abs =
|
|
7340
|
+
const abs = import_node_path37.default.join(dir, filename);
|
|
7008
7341
|
if (!await exists(abs)) continue;
|
|
7009
7342
|
const raw = await import_node_fs20.promises.readFile(abs, "utf8");
|
|
7010
7343
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7048,8 +7381,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
7048
7381
|
source: anchorId,
|
|
7049
7382
|
target: node.id,
|
|
7050
7383
|
type: edgeType,
|
|
7051
|
-
provenance:
|
|
7052
|
-
confidence: (0,
|
|
7384
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7385
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7053
7386
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
7054
7387
|
};
|
|
7055
7388
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7067,11 +7400,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7067
7400
|
try {
|
|
7068
7401
|
read = await readWranglerConfig(service.dir);
|
|
7069
7402
|
} catch (err) {
|
|
7070
|
-
recordExtractionError("infra cloudflare",
|
|
7403
|
+
recordExtractionError("infra cloudflare", import_node_path37.default.relative(scanPath, service.dir), err);
|
|
7071
7404
|
continue;
|
|
7072
7405
|
}
|
|
7073
7406
|
if (!read || !read.config.name) continue;
|
|
7074
|
-
const evidenceFile = toPosix2(
|
|
7407
|
+
const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, read.relFile)));
|
|
7075
7408
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
7076
7409
|
}
|
|
7077
7410
|
for (const worker of discovered) {
|
|
@@ -7083,7 +7416,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7083
7416
|
}
|
|
7084
7417
|
let anchorId = service.node.id;
|
|
7085
7418
|
if (config.main) {
|
|
7086
|
-
const entryRelPath = toPosix2(
|
|
7419
|
+
const entryRelPath = toPosix2(import_node_path37.default.normalize(config.main));
|
|
7087
7420
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7088
7421
|
graph,
|
|
7089
7422
|
service.pkg.name,
|
|
@@ -7110,15 +7443,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7110
7443
|
nodesAdded++;
|
|
7111
7444
|
}
|
|
7112
7445
|
if (runtimeNode.id !== anchorId) {
|
|
7113
|
-
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
7446
|
+
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types26.EdgeType.RUNS_ON);
|
|
7114
7447
|
if (!graph.hasEdge(runsOnId)) {
|
|
7115
7448
|
const edge = {
|
|
7116
7449
|
id: runsOnId,
|
|
7117
7450
|
source: anchorId,
|
|
7118
7451
|
target: runtimeNode.id,
|
|
7119
|
-
type:
|
|
7120
|
-
provenance:
|
|
7121
|
-
confidence: (0,
|
|
7452
|
+
type: import_types26.EdgeType.RUNS_ON,
|
|
7453
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7454
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7122
7455
|
evidence: {
|
|
7123
7456
|
file: evidenceFile,
|
|
7124
7457
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -7132,7 +7465,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7132
7465
|
const result = addResourceEdge(
|
|
7133
7466
|
graph,
|
|
7134
7467
|
anchorId,
|
|
7135
|
-
|
|
7468
|
+
import_types26.EdgeType.CONNECTS_TO,
|
|
7136
7469
|
"cloudflare-route",
|
|
7137
7470
|
route,
|
|
7138
7471
|
evidenceFile,
|
|
@@ -7156,7 +7489,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7156
7489
|
const result = addResourceEdge(
|
|
7157
7490
|
graph,
|
|
7158
7491
|
anchorId,
|
|
7159
|
-
|
|
7492
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7160
7493
|
group.kind,
|
|
7161
7494
|
name,
|
|
7162
7495
|
evidenceFile,
|
|
@@ -7170,7 +7503,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7170
7503
|
const result = addResourceEdge(
|
|
7171
7504
|
graph,
|
|
7172
7505
|
anchorId,
|
|
7173
|
-
|
|
7506
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7174
7507
|
"cloudflare-cron",
|
|
7175
7508
|
cron,
|
|
7176
7509
|
evidenceFile,
|
|
@@ -7183,7 +7516,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7183
7516
|
const result = addResourceEdge(
|
|
7184
7517
|
graph,
|
|
7185
7518
|
anchorId,
|
|
7186
|
-
|
|
7519
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7187
7520
|
"cloudflare-env-var",
|
|
7188
7521
|
varName,
|
|
7189
7522
|
evidenceFile,
|
|
@@ -7196,15 +7529,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7196
7529
|
if (!svc.service) continue;
|
|
7197
7530
|
const target = workerIndex.get(svc.service);
|
|
7198
7531
|
if (target && target.anchorId !== anchorId) {
|
|
7199
|
-
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId,
|
|
7532
|
+
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types26.EdgeType.CALLS);
|
|
7200
7533
|
if (!graph.hasEdge(edgeId)) {
|
|
7201
7534
|
const edge = {
|
|
7202
7535
|
id: edgeId,
|
|
7203
7536
|
source: anchorId,
|
|
7204
7537
|
target: target.anchorId,
|
|
7205
|
-
type:
|
|
7206
|
-
provenance:
|
|
7207
|
-
confidence: (0,
|
|
7538
|
+
type: import_types26.EdgeType.CALLS,
|
|
7539
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7540
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7208
7541
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
7209
7542
|
};
|
|
7210
7543
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7215,7 +7548,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7215
7548
|
const result = addResourceEdge(
|
|
7216
7549
|
graph,
|
|
7217
7550
|
anchorId,
|
|
7218
|
-
|
|
7551
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7219
7552
|
"cloudflare-service-binding",
|
|
7220
7553
|
svc.service,
|
|
7221
7554
|
evidenceFile,
|
|
@@ -7231,12 +7564,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7231
7564
|
// src/extract/infra/vercel.ts
|
|
7232
7565
|
init_cjs_shims();
|
|
7233
7566
|
var import_node_fs21 = require("fs");
|
|
7234
|
-
var
|
|
7235
|
-
var
|
|
7567
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
7568
|
+
var import_types27 = require("@neat.is/types");
|
|
7236
7569
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
7237
7570
|
async function readVercelConfig(dir) {
|
|
7238
7571
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
7239
|
-
const abs =
|
|
7572
|
+
const abs = import_node_path38.default.join(dir, filename);
|
|
7240
7573
|
if (!await exists(abs)) continue;
|
|
7241
7574
|
const raw = await import_node_fs21.promises.readFile(abs, "utf8");
|
|
7242
7575
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7245,7 +7578,7 @@ async function readVercelConfig(dir) {
|
|
|
7245
7578
|
return null;
|
|
7246
7579
|
}
|
|
7247
7580
|
async function readLinkedProjectName(dir) {
|
|
7248
|
-
const abs =
|
|
7581
|
+
const abs = import_node_path38.default.join(dir, ".vercel", "project.json");
|
|
7249
7582
|
if (!await exists(abs)) return void 0;
|
|
7250
7583
|
const parsed = JSON.parse(await import_node_fs21.promises.readFile(abs, "utf8"));
|
|
7251
7584
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -7263,7 +7596,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7263
7596
|
read = await readVercelConfig(service.dir);
|
|
7264
7597
|
projectName = await readLinkedProjectName(service.dir);
|
|
7265
7598
|
} catch (err) {
|
|
7266
|
-
recordExtractionError("infra vercel",
|
|
7599
|
+
recordExtractionError("infra vercel", import_node_path38.default.relative(scanPath, service.dir), err);
|
|
7267
7600
|
continue;
|
|
7268
7601
|
}
|
|
7269
7602
|
if (!read && !projectName) continue;
|
|
@@ -7279,7 +7612,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7279
7612
|
const anchorId = service.node.id;
|
|
7280
7613
|
if (!read) continue;
|
|
7281
7614
|
const { config, relFile, raw } = read;
|
|
7282
|
-
const evidenceFile = toPosix2(
|
|
7615
|
+
const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
|
|
7283
7616
|
const add = (edgeType, kind, name) => {
|
|
7284
7617
|
if (!name) return;
|
|
7285
7618
|
const result = emitPlatformResourceEdge(
|
|
@@ -7295,12 +7628,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7295
7628
|
nodesAdded += result.nodesAdded;
|
|
7296
7629
|
edgesAdded += result.edgesAdded;
|
|
7297
7630
|
};
|
|
7298
|
-
add(
|
|
7299
|
-
for (const cron of config.crons ?? []) add(
|
|
7300
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
7301
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
7631
|
+
add(import_types27.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
7632
|
+
for (const cron of config.crons ?? []) add(import_types27.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
7633
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7634
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7302
7635
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
7303
|
-
add(
|
|
7636
|
+
add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
7304
7637
|
}
|
|
7305
7638
|
}
|
|
7306
7639
|
return { nodesAdded, edgesAdded };
|
|
@@ -7309,13 +7642,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7309
7642
|
// src/extract/infra/railway.ts
|
|
7310
7643
|
init_cjs_shims();
|
|
7311
7644
|
var import_node_fs22 = require("fs");
|
|
7312
|
-
var
|
|
7645
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
7313
7646
|
var import_smol_toml3 = require("smol-toml");
|
|
7314
|
-
var
|
|
7647
|
+
var import_types28 = require("@neat.is/types");
|
|
7315
7648
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
7316
7649
|
async function readRailwayConfig(dir) {
|
|
7317
7650
|
for (const filename of RAILWAY_FILENAMES) {
|
|
7318
|
-
const abs =
|
|
7651
|
+
const abs = import_node_path39.default.join(dir, filename);
|
|
7319
7652
|
if (!await exists(abs)) continue;
|
|
7320
7653
|
const raw = await import_node_fs22.promises.readFile(abs, "utf8");
|
|
7321
7654
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7331,7 +7664,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7331
7664
|
try {
|
|
7332
7665
|
read = await readRailwayConfig(service.dir);
|
|
7333
7666
|
} catch (err) {
|
|
7334
|
-
recordExtractionError("infra railway",
|
|
7667
|
+
recordExtractionError("infra railway", import_node_path39.default.relative(scanPath, service.dir), err);
|
|
7335
7668
|
continue;
|
|
7336
7669
|
}
|
|
7337
7670
|
if (!read) continue;
|
|
@@ -7341,7 +7674,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7341
7674
|
}
|
|
7342
7675
|
const anchorId = service.node.id;
|
|
7343
7676
|
const { config, relFile, raw } = read;
|
|
7344
|
-
const evidenceFile = toPosix2(
|
|
7677
|
+
const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
|
|
7345
7678
|
const add = (edgeType, kind, name) => {
|
|
7346
7679
|
if (!name) return;
|
|
7347
7680
|
const result = emitPlatformResourceEdge(
|
|
@@ -7357,9 +7690,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7357
7690
|
nodesAdded += result.nodesAdded;
|
|
7358
7691
|
edgesAdded += result.edgesAdded;
|
|
7359
7692
|
};
|
|
7360
|
-
add(
|
|
7361
|
-
add(
|
|
7362
|
-
add(
|
|
7693
|
+
add(import_types28.EdgeType.RUNS_ON, "railway", "railway");
|
|
7694
|
+
add(import_types28.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
7695
|
+
add(import_types28.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
7363
7696
|
}
|
|
7364
7697
|
return { nodesAdded, edgesAdded };
|
|
7365
7698
|
}
|
|
@@ -7367,12 +7700,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7367
7700
|
// src/extract/infra/supabase.ts
|
|
7368
7701
|
init_cjs_shims();
|
|
7369
7702
|
var import_node_fs23 = require("fs");
|
|
7370
|
-
var
|
|
7703
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
7371
7704
|
var import_smol_toml4 = require("smol-toml");
|
|
7372
|
-
var
|
|
7705
|
+
var import_types29 = require("@neat.is/types");
|
|
7373
7706
|
async function readSupabaseConfig(dir) {
|
|
7374
|
-
const relFile =
|
|
7375
|
-
const abs =
|
|
7707
|
+
const relFile = import_node_path40.default.join("supabase", "config.toml");
|
|
7708
|
+
const abs = import_node_path40.default.join(dir, relFile);
|
|
7376
7709
|
if (!await exists(abs)) return null;
|
|
7377
7710
|
const raw = await import_node_fs23.promises.readFile(abs, "utf8");
|
|
7378
7711
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -7386,7 +7719,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7386
7719
|
try {
|
|
7387
7720
|
read = await readSupabaseConfig(service.dir);
|
|
7388
7721
|
} catch (err) {
|
|
7389
|
-
recordExtractionError("infra supabase",
|
|
7722
|
+
recordExtractionError("infra supabase", import_node_path40.default.relative(scanPath, service.dir), err);
|
|
7390
7723
|
continue;
|
|
7391
7724
|
}
|
|
7392
7725
|
if (!read) continue;
|
|
@@ -7401,7 +7734,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7401
7734
|
});
|
|
7402
7735
|
}
|
|
7403
7736
|
const anchorId = service.node.id;
|
|
7404
|
-
const evidenceFile = toPosix2(
|
|
7737
|
+
const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
|
|
7405
7738
|
const add = (edgeType, kind, name) => {
|
|
7406
7739
|
if (!name) return;
|
|
7407
7740
|
const result = emitPlatformResourceEdge(
|
|
@@ -7417,10 +7750,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7417
7750
|
nodesAdded += result.nodesAdded;
|
|
7418
7751
|
edgesAdded += result.edgesAdded;
|
|
7419
7752
|
};
|
|
7420
|
-
add(
|
|
7421
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
7422
|
-
if (config.storage) add(
|
|
7423
|
-
if (config.auth) add(
|
|
7753
|
+
add(import_types29.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
7754
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types29.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
7755
|
+
if (config.storage) add(import_types29.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
7756
|
+
if (config.auth) add(import_types29.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
7424
7757
|
}
|
|
7425
7758
|
return { nodesAdded, edgesAdded };
|
|
7426
7759
|
}
|
|
@@ -7442,17 +7775,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
7442
7775
|
}
|
|
7443
7776
|
|
|
7444
7777
|
// src/extract/index.ts
|
|
7445
|
-
var
|
|
7778
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
7446
7779
|
|
|
7447
7780
|
// src/extract/retire.ts
|
|
7448
7781
|
init_cjs_shims();
|
|
7449
7782
|
var import_node_fs24 = require("fs");
|
|
7450
|
-
var
|
|
7451
|
-
var
|
|
7783
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
7784
|
+
var import_types30 = require("@neat.is/types");
|
|
7452
7785
|
function dropOrphanedFileNodes(graph) {
|
|
7453
7786
|
const orphans = [];
|
|
7454
7787
|
graph.forEachNode((id, attrs) => {
|
|
7455
|
-
if (attrs.type !==
|
|
7788
|
+
if (attrs.type !== import_types30.NodeType.FileNode) return;
|
|
7456
7789
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
7457
7790
|
orphans.push(id);
|
|
7458
7791
|
}
|
|
@@ -7465,7 +7798,7 @@ function retireEdgesByFile(graph, file) {
|
|
|
7465
7798
|
const toDrop = [];
|
|
7466
7799
|
graph.forEachEdge((id, attrs) => {
|
|
7467
7800
|
const edge = attrs;
|
|
7468
|
-
if (edge.provenance !==
|
|
7801
|
+
if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
|
|
7469
7802
|
if (!edge.evidence?.file) return;
|
|
7470
7803
|
if (edge.evidence.file === normalized) toDrop.push(id);
|
|
7471
7804
|
});
|
|
@@ -7478,14 +7811,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
7478
7811
|
const bases = [scanPath, ...serviceDirs];
|
|
7479
7812
|
graph.forEachEdge((id, attrs) => {
|
|
7480
7813
|
const edge = attrs;
|
|
7481
|
-
if (edge.provenance !==
|
|
7814
|
+
if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
|
|
7482
7815
|
const evidenceFile = edge.evidence?.file;
|
|
7483
7816
|
if (!evidenceFile) return;
|
|
7484
|
-
if (
|
|
7817
|
+
if (import_node_path41.default.isAbsolute(evidenceFile)) {
|
|
7485
7818
|
if (!(0, import_node_fs24.existsSync)(evidenceFile)) toDrop.push(id);
|
|
7486
7819
|
return;
|
|
7487
7820
|
}
|
|
7488
|
-
const found = bases.some((base) => (0, import_node_fs24.existsSync)(
|
|
7821
|
+
const found = bases.some((base) => (0, import_node_fs24.existsSync)(import_node_path41.default.join(base, evidenceFile)));
|
|
7489
7822
|
if (!found) toDrop.push(id);
|
|
7490
7823
|
});
|
|
7491
7824
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -7527,7 +7860,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7527
7860
|
}
|
|
7528
7861
|
const droppedEntries = drainDroppedExtracted();
|
|
7529
7862
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
7530
|
-
const rejectedPath =
|
|
7863
|
+
const rejectedPath = import_node_path42.default.join(import_node_path42.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
7531
7864
|
try {
|
|
7532
7865
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
7533
7866
|
} catch (err) {
|
|
@@ -7561,39 +7894,39 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7561
7894
|
|
|
7562
7895
|
// src/divergences.ts
|
|
7563
7896
|
init_cjs_shims();
|
|
7564
|
-
var
|
|
7897
|
+
var import_types31 = require("@neat.is/types");
|
|
7565
7898
|
function bucketKey(source, target, type) {
|
|
7566
7899
|
return `${type}|${source}|${target}`;
|
|
7567
7900
|
}
|
|
7568
7901
|
function bucketSourceFor(graph, edge) {
|
|
7569
|
-
if (edge.type !==
|
|
7570
|
-
const parsed = (0,
|
|
7902
|
+
if (edge.type !== import_types31.EdgeType.CONNECTS_TO) return edge.source;
|
|
7903
|
+
const parsed = (0, import_types31.parseFileId)(edge.source);
|
|
7571
7904
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
7572
7905
|
const target = graph.getNodeAttributes(edge.target);
|
|
7573
|
-
if (target.type !==
|
|
7574
|
-
return (0,
|
|
7906
|
+
if (target.type !== import_types31.NodeType.DatabaseNode) return edge.source;
|
|
7907
|
+
return (0, import_types31.serviceId)(parsed.service);
|
|
7575
7908
|
}
|
|
7576
7909
|
function bucketEdges(graph) {
|
|
7577
7910
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
7578
7911
|
graph.forEachEdge((id, attrs) => {
|
|
7579
7912
|
const e = attrs;
|
|
7580
|
-
const parsed = (0,
|
|
7913
|
+
const parsed = (0, import_types31.parseEdgeId)(id);
|
|
7581
7914
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
7582
7915
|
const source = bucketSourceFor(graph, e);
|
|
7583
7916
|
const key = bucketKey(source, e.target, e.type);
|
|
7584
7917
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
7585
7918
|
switch (provenance) {
|
|
7586
|
-
case
|
|
7919
|
+
case import_types31.Provenance.EXTRACTED:
|
|
7587
7920
|
cur.extracted = e;
|
|
7588
7921
|
break;
|
|
7589
|
-
case
|
|
7922
|
+
case import_types31.Provenance.OBSERVED:
|
|
7590
7923
|
cur.observed = e;
|
|
7591
7924
|
break;
|
|
7592
|
-
case
|
|
7925
|
+
case import_types31.Provenance.INFERRED:
|
|
7593
7926
|
cur.inferred = e;
|
|
7594
7927
|
break;
|
|
7595
7928
|
default:
|
|
7596
|
-
if (e.provenance ===
|
|
7929
|
+
if (e.provenance === import_types31.Provenance.STALE) cur.stale = e;
|
|
7597
7930
|
}
|
|
7598
7931
|
buckets2.set(key, cur);
|
|
7599
7932
|
});
|
|
@@ -7602,12 +7935,12 @@ function bucketEdges(graph) {
|
|
|
7602
7935
|
function nodeIsFrontier(graph, nodeId) {
|
|
7603
7936
|
if (!graph.hasNode(nodeId)) return false;
|
|
7604
7937
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
7605
|
-
return attrs.type ===
|
|
7938
|
+
return attrs.type === import_types31.NodeType.FrontierNode;
|
|
7606
7939
|
}
|
|
7607
7940
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
7608
7941
|
if (!graph.hasNode(nodeId)) return false;
|
|
7609
7942
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
7610
|
-
return attrs.type ===
|
|
7943
|
+
return attrs.type === import_types31.NodeType.WebSocketChannelNode;
|
|
7611
7944
|
}
|
|
7612
7945
|
function clampConfidence(n) {
|
|
7613
7946
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -7627,14 +7960,14 @@ function gradedConfidence(edge) {
|
|
|
7627
7960
|
return clampConfidence(confidenceForEdge(edge));
|
|
7628
7961
|
}
|
|
7629
7962
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7963
|
+
import_types31.EdgeType.CALLS,
|
|
7964
|
+
import_types31.EdgeType.CONNECTS_TO,
|
|
7965
|
+
import_types31.EdgeType.PUBLISHES_TO,
|
|
7966
|
+
import_types31.EdgeType.CONSUMES_FROM
|
|
7634
7967
|
]);
|
|
7635
7968
|
function detectMissingDivergences(graph, bucket) {
|
|
7636
7969
|
const out = [];
|
|
7637
|
-
if (bucket.type ===
|
|
7970
|
+
if (bucket.type === import_types31.EdgeType.CONTAINS) return out;
|
|
7638
7971
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
7639
7972
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
7640
7973
|
out.push({
|
|
@@ -7675,7 +8008,7 @@ function declaredHostFor(svc) {
|
|
|
7675
8008
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
7676
8009
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
7677
8010
|
const e = graph.getEdgeAttributes(edgeId);
|
|
7678
|
-
if (e.type ===
|
|
8011
|
+
if (e.type === import_types31.EdgeType.CONFIGURED_BY && e.provenance === import_types31.Provenance.EXTRACTED) {
|
|
7679
8012
|
return true;
|
|
7680
8013
|
}
|
|
7681
8014
|
}
|
|
@@ -7688,10 +8021,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
7688
8021
|
const out = [];
|
|
7689
8022
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
7690
8023
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
7691
|
-
if (edge.type !==
|
|
7692
|
-
if (edge.provenance !==
|
|
8024
|
+
if (edge.type !== import_types31.EdgeType.CONNECTS_TO) continue;
|
|
8025
|
+
if (edge.provenance !== import_types31.Provenance.OBSERVED) continue;
|
|
7693
8026
|
const target = graph.getNodeAttributes(edge.target);
|
|
7694
|
-
if (target.type !==
|
|
8027
|
+
if (target.type !== import_types31.NodeType.DatabaseNode) continue;
|
|
7695
8028
|
const observedHost = target.host?.trim();
|
|
7696
8029
|
if (!observedHost) continue;
|
|
7697
8030
|
if (observedHost === declaredHost) continue;
|
|
@@ -7713,10 +8046,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
7713
8046
|
const deps = svc.dependencies ?? {};
|
|
7714
8047
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
7715
8048
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
7716
|
-
if (edge.type !==
|
|
7717
|
-
if (edge.provenance !==
|
|
8049
|
+
if (edge.type !== import_types31.EdgeType.CONNECTS_TO) continue;
|
|
8050
|
+
if (edge.provenance !== import_types31.Provenance.OBSERVED) continue;
|
|
7718
8051
|
const target = graph.getNodeAttributes(edge.target);
|
|
7719
|
-
if (target.type !==
|
|
8052
|
+
if (target.type !== import_types31.NodeType.DatabaseNode) continue;
|
|
7720
8053
|
for (const pair of compatPairs()) {
|
|
7721
8054
|
if (pair.engine !== target.engine) continue;
|
|
7722
8055
|
const declared = deps[pair.driver];
|
|
@@ -7775,7 +8108,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
7775
8108
|
for (const d of all) {
|
|
7776
8109
|
if (d.type !== "host-mismatch") continue;
|
|
7777
8110
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
7778
|
-
declaredHalf.add((0,
|
|
8111
|
+
declaredHalf.add((0, import_types31.databaseId)(d.extractedHost));
|
|
7779
8112
|
}
|
|
7780
8113
|
if (observedHalf.size === 0) return all;
|
|
7781
8114
|
return all.filter((d) => {
|
|
@@ -7794,7 +8127,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
7794
8127
|
}
|
|
7795
8128
|
graph.forEachNode((nodeId, attrs) => {
|
|
7796
8129
|
const n = attrs;
|
|
7797
|
-
if (n.type !==
|
|
8130
|
+
if (n.type !== import_types31.NodeType.ServiceNode) return;
|
|
7798
8131
|
const svc = n;
|
|
7799
8132
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
7800
8133
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -7828,7 +8161,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
7828
8161
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
7829
8162
|
return a.target.localeCompare(b.target);
|
|
7830
8163
|
});
|
|
7831
|
-
return
|
|
8164
|
+
return import_types31.DivergenceResultSchema.parse({
|
|
7832
8165
|
divergences: filtered,
|
|
7833
8166
|
totalAffected: filtered.length,
|
|
7834
8167
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -7838,8 +8171,8 @@ function computeDivergences(graph, opts = {}) {
|
|
|
7838
8171
|
// src/persist.ts
|
|
7839
8172
|
init_cjs_shims();
|
|
7840
8173
|
var import_node_fs25 = require("fs");
|
|
7841
|
-
var
|
|
7842
|
-
var
|
|
8174
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
8175
|
+
var import_types32 = require("@neat.is/types");
|
|
7843
8176
|
var SCHEMA_VERSION = 4;
|
|
7844
8177
|
function migrateV1ToV2(payload) {
|
|
7845
8178
|
const nodes = payload.graph.nodes;
|
|
@@ -7861,12 +8194,12 @@ function migrateV2ToV3(payload) {
|
|
|
7861
8194
|
for (const edge of edges) {
|
|
7862
8195
|
const attrs = edge.attributes;
|
|
7863
8196
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
7864
|
-
attrs.provenance =
|
|
8197
|
+
attrs.provenance = import_types32.Provenance.OBSERVED;
|
|
7865
8198
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
7866
8199
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
7867
8200
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
7868
8201
|
if (type && source && target) {
|
|
7869
|
-
const newId = (0,
|
|
8202
|
+
const newId = (0, import_types32.observedEdgeId)(source, target, type);
|
|
7870
8203
|
attrs.id = newId;
|
|
7871
8204
|
if (edge.key) edge.key = newId;
|
|
7872
8205
|
}
|
|
@@ -7875,7 +8208,7 @@ function migrateV2ToV3(payload) {
|
|
|
7875
8208
|
return { ...payload, schemaVersion: 3 };
|
|
7876
8209
|
}
|
|
7877
8210
|
async function ensureDir(filePath) {
|
|
7878
|
-
await import_node_fs25.promises.mkdir(
|
|
8211
|
+
await import_node_fs25.promises.mkdir(import_node_path43.default.dirname(filePath), { recursive: true });
|
|
7879
8212
|
}
|
|
7880
8213
|
async function saveGraphToDisk(graph, outPath) {
|
|
7881
8214
|
await ensureDir(outPath);
|
|
@@ -7957,7 +8290,7 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
7957
8290
|
// src/gitignore.ts
|
|
7958
8291
|
init_cjs_shims();
|
|
7959
8292
|
var import_node_fs26 = require("fs");
|
|
7960
|
-
var
|
|
8293
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
7961
8294
|
var NEAT_OUT_LINE = "neat-out/";
|
|
7962
8295
|
var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
|
|
7963
8296
|
function isNeatOutLine(line) {
|
|
@@ -7965,7 +8298,7 @@ function isNeatOutLine(line) {
|
|
|
7965
8298
|
return trimmed === "neat-out/" || trimmed === "neat-out";
|
|
7966
8299
|
}
|
|
7967
8300
|
async function ensureNeatOutIgnored(projectDir) {
|
|
7968
|
-
const file =
|
|
8301
|
+
const file = import_node_path44.default.join(projectDir, ".gitignore");
|
|
7969
8302
|
let existing = null;
|
|
7970
8303
|
try {
|
|
7971
8304
|
existing = await import_node_fs26.promises.readFile(file, "utf8");
|
|
@@ -7992,7 +8325,7 @@ ${NEAT_OUT_LINE}
|
|
|
7992
8325
|
|
|
7993
8326
|
// src/summary.ts
|
|
7994
8327
|
init_cjs_shims();
|
|
7995
|
-
var
|
|
8328
|
+
var import_types33 = require("@neat.is/types");
|
|
7996
8329
|
function renderOtelEnvBlock() {
|
|
7997
8330
|
return [
|
|
7998
8331
|
"for prod OTel routing, set these in your deploy platform's env:",
|
|
@@ -8002,19 +8335,19 @@ function renderOtelEnvBlock() {
|
|
|
8002
8335
|
}
|
|
8003
8336
|
function findIncompatServices(nodes) {
|
|
8004
8337
|
return nodes.filter(
|
|
8005
|
-
(n) => n.type ===
|
|
8338
|
+
(n) => n.type === import_types33.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
|
|
8006
8339
|
);
|
|
8007
8340
|
}
|
|
8008
8341
|
function servicesWithoutObserved(nodes, edges) {
|
|
8009
8342
|
const seen = /* @__PURE__ */ new Set();
|
|
8010
8343
|
for (const e of edges) {
|
|
8011
|
-
if (e.provenance ===
|
|
8344
|
+
if (e.provenance === import_types33.Provenance.OBSERVED) {
|
|
8012
8345
|
seen.add(e.source);
|
|
8013
8346
|
seen.add(e.target);
|
|
8014
8347
|
}
|
|
8015
8348
|
}
|
|
8016
8349
|
return nodes.filter(
|
|
8017
|
-
(n) => n.type ===
|
|
8350
|
+
(n) => n.type === import_types33.NodeType.ServiceNode && !seen.has(n.id)
|
|
8018
8351
|
);
|
|
8019
8352
|
}
|
|
8020
8353
|
function formatDivergence(d) {
|
|
@@ -8089,26 +8422,26 @@ function formatIncompat(inc) {
|
|
|
8089
8422
|
// src/watch.ts
|
|
8090
8423
|
init_cjs_shims();
|
|
8091
8424
|
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
8092
|
-
var
|
|
8425
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
8093
8426
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
8094
8427
|
|
|
8095
8428
|
// src/api.ts
|
|
8096
8429
|
init_cjs_shims();
|
|
8097
8430
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
8098
8431
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
8099
|
-
var
|
|
8432
|
+
var import_types35 = require("@neat.is/types");
|
|
8100
8433
|
|
|
8101
8434
|
// src/extend/index.ts
|
|
8102
8435
|
init_cjs_shims();
|
|
8103
8436
|
var import_node_fs28 = require("fs");
|
|
8104
|
-
var
|
|
8437
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
8105
8438
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
8106
8439
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
8107
8440
|
|
|
8108
8441
|
// src/installers/package-manager.ts
|
|
8109
8442
|
init_cjs_shims();
|
|
8110
8443
|
var import_node_fs27 = require("fs");
|
|
8111
|
-
var
|
|
8444
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
8112
8445
|
var import_node_child_process = require("child_process");
|
|
8113
8446
|
var LOCKFILE_PRIORITY = [
|
|
8114
8447
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -8130,22 +8463,22 @@ async function exists2(p) {
|
|
|
8130
8463
|
}
|
|
8131
8464
|
}
|
|
8132
8465
|
async function detectPackageManager(serviceDir) {
|
|
8133
|
-
let dir =
|
|
8466
|
+
let dir = import_node_path45.default.resolve(serviceDir);
|
|
8134
8467
|
const stops = /* @__PURE__ */ new Set();
|
|
8135
8468
|
for (let i = 0; i < 64; i++) {
|
|
8136
8469
|
if (stops.has(dir)) break;
|
|
8137
8470
|
stops.add(dir);
|
|
8138
8471
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
8139
|
-
const lockPath =
|
|
8472
|
+
const lockPath = import_node_path45.default.join(dir, candidate.lockfile);
|
|
8140
8473
|
if (await exists2(lockPath)) {
|
|
8141
8474
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
8142
8475
|
}
|
|
8143
8476
|
}
|
|
8144
|
-
const parent =
|
|
8477
|
+
const parent = import_node_path45.default.dirname(dir);
|
|
8145
8478
|
if (parent === dir) break;
|
|
8146
8479
|
dir = parent;
|
|
8147
8480
|
}
|
|
8148
|
-
return { pm: "npm", cwd:
|
|
8481
|
+
return { pm: "npm", cwd: import_node_path45.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
8149
8482
|
}
|
|
8150
8483
|
async function runPackageManagerInstall(cmd) {
|
|
8151
8484
|
return new Promise((resolve) => {
|
|
@@ -8194,22 +8527,53 @@ async function fileExists2(p) {
|
|
|
8194
8527
|
}
|
|
8195
8528
|
}
|
|
8196
8529
|
async function readPackageJson(scanPath) {
|
|
8197
|
-
const pkgPath =
|
|
8530
|
+
const pkgPath = import_node_path46.default.join(scanPath, "package.json");
|
|
8198
8531
|
const raw = await import_node_fs28.promises.readFile(pkgPath, "utf8");
|
|
8199
8532
|
return JSON.parse(raw);
|
|
8200
8533
|
}
|
|
8534
|
+
var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
8535
|
+
"node_modules",
|
|
8536
|
+
"dist",
|
|
8537
|
+
"build",
|
|
8538
|
+
"out",
|
|
8539
|
+
"coverage",
|
|
8540
|
+
"neat-out"
|
|
8541
|
+
]);
|
|
8201
8542
|
async function findHookFiles(scanPath) {
|
|
8202
|
-
const
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8543
|
+
const found = [];
|
|
8544
|
+
const walk3 = async (dir) => {
|
|
8545
|
+
const entries = await import_node_fs28.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
8546
|
+
for (const entry2 of entries) {
|
|
8547
|
+
if (entry2.isDirectory()) {
|
|
8548
|
+
if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
|
|
8549
|
+
await walk3(import_node_path46.default.join(dir, entry2.name));
|
|
8550
|
+
} else if (entry2.isFile()) {
|
|
8551
|
+
if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
|
|
8552
|
+
const rel = import_node_path46.default.relative(scanPath, import_node_path46.default.join(dir, entry2.name));
|
|
8553
|
+
found.push(rel.split(import_node_path46.default.sep).join("/"));
|
|
8554
|
+
}
|
|
8555
|
+
}
|
|
8556
|
+
}
|
|
8557
|
+
};
|
|
8558
|
+
await walk3(scanPath);
|
|
8559
|
+
return found.sort();
|
|
8560
|
+
}
|
|
8561
|
+
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
8562
|
+
let fallback = null;
|
|
8563
|
+
for (const file of hookFiles) {
|
|
8564
|
+
const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(scanPath, file), "utf8");
|
|
8565
|
+
const patched = splicedContent(content, snippet2);
|
|
8566
|
+
if (patched !== null) return { file, content, patched };
|
|
8567
|
+
if (fallback === null) fallback = { file, content };
|
|
8568
|
+
}
|
|
8569
|
+
return { file: fallback.file, content: fallback.content, patched: null };
|
|
8206
8570
|
}
|
|
8207
8571
|
function extendLogPath() {
|
|
8208
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
8572
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path46.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
8209
8573
|
}
|
|
8210
8574
|
async function appendExtendLog(entry2) {
|
|
8211
8575
|
const logPath = extendLogPath();
|
|
8212
|
-
await import_node_fs28.promises.mkdir(
|
|
8576
|
+
await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(logPath), { recursive: true });
|
|
8213
8577
|
await import_node_fs28.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
8214
8578
|
}
|
|
8215
8579
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -8268,7 +8632,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
8268
8632
|
}
|
|
8269
8633
|
async function describeProjectInstrumentation(ctx) {
|
|
8270
8634
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8271
|
-
const envNeat = await fileExists2(
|
|
8635
|
+
const envNeat = await fileExists2(import_node_path46.default.join(ctx.scanPath, ".env.neat"));
|
|
8272
8636
|
const registryInstrPackages = new Set(
|
|
8273
8637
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
8274
8638
|
);
|
|
@@ -8290,16 +8654,22 @@ async function applyExtension(ctx, args, options) {
|
|
|
8290
8654
|
);
|
|
8291
8655
|
}
|
|
8292
8656
|
for (const file of hookFiles) {
|
|
8293
|
-
const content = await import_node_fs28.promises.readFile(
|
|
8657
|
+
const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(ctx.scanPath, file), "utf8");
|
|
8294
8658
|
if (content.includes(args.registration_snippet)) {
|
|
8295
8659
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
8296
8660
|
}
|
|
8297
8661
|
}
|
|
8298
|
-
const
|
|
8299
|
-
|
|
8662
|
+
const primary = await pickPrimaryHookFile(ctx.scanPath, hookFiles, args.registration_snippet);
|
|
8663
|
+
if (primary.patched === null) {
|
|
8664
|
+
throw new Error(
|
|
8665
|
+
`Could not find instrumentation insertion point in ${hookFiles.join(", ")}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
|
|
8666
|
+
);
|
|
8667
|
+
}
|
|
8668
|
+
const primaryFile = primary.file;
|
|
8669
|
+
const primaryPath = import_node_path46.default.join(ctx.scanPath, primaryFile);
|
|
8300
8670
|
const filesTouched = [];
|
|
8301
8671
|
const depsAdded = [];
|
|
8302
|
-
const pkgPath =
|
|
8672
|
+
const pkgPath = import_node_path46.default.join(ctx.scanPath, "package.json");
|
|
8303
8673
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
8304
8674
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
8305
8675
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -8307,14 +8677,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
8307
8677
|
filesTouched.push("package.json");
|
|
8308
8678
|
depsAdded.push(`${args.instrumentation_package}@${args.version}`);
|
|
8309
8679
|
}
|
|
8310
|
-
|
|
8311
|
-
const patched = splicedContent(hookContent, args.registration_snippet);
|
|
8312
|
-
if (!patched) {
|
|
8313
|
-
throw new Error(
|
|
8314
|
-
`Could not find instrumentation insertion point in ${primaryFile}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
|
|
8315
|
-
);
|
|
8316
|
-
}
|
|
8317
|
-
await import_node_fs28.promises.writeFile(primaryPath, patched, "utf8");
|
|
8680
|
+
await import_node_fs28.promises.writeFile(primaryPath, primary.patched, "utf8");
|
|
8318
8681
|
filesTouched.push(primaryFile);
|
|
8319
8682
|
const cmd = await detectPackageManager(ctx.scanPath);
|
|
8320
8683
|
const installer = options?.runInstall ?? runPackageManagerInstall;
|
|
@@ -8345,7 +8708,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
8345
8708
|
};
|
|
8346
8709
|
}
|
|
8347
8710
|
for (const file of hookFiles) {
|
|
8348
|
-
const content = await import_node_fs28.promises.readFile(
|
|
8711
|
+
const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(ctx.scanPath, file), "utf8");
|
|
8349
8712
|
if (content.includes(args.registration_snippet)) {
|
|
8350
8713
|
return {
|
|
8351
8714
|
library: args.library,
|
|
@@ -8356,7 +8719,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
8356
8719
|
};
|
|
8357
8720
|
}
|
|
8358
8721
|
}
|
|
8359
|
-
const
|
|
8722
|
+
const primary = await pickPrimaryHookFile(ctx.scanPath, hookFiles, args.registration_snippet);
|
|
8360
8723
|
const filesTouched = [];
|
|
8361
8724
|
const depsToAdd = [];
|
|
8362
8725
|
let packageJsonPatch = {};
|
|
@@ -8367,10 +8730,8 @@ async function dryRunExtension(ctx, args) {
|
|
|
8367
8730
|
depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
|
|
8368
8731
|
filesTouched.push("package.json");
|
|
8369
8732
|
}
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
if (patched) {
|
|
8373
|
-
filesTouched.push(primaryFile);
|
|
8733
|
+
if (primary.patched !== null) {
|
|
8734
|
+
filesTouched.push(primary.file);
|
|
8374
8735
|
templatePatch = `+ ${args.registration_snippet}`;
|
|
8375
8736
|
} else {
|
|
8376
8737
|
templatePatch = "Could not find insertion point in hook file.";
|
|
@@ -8388,7 +8749,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8388
8749
|
if (!match) {
|
|
8389
8750
|
return { undone: false, message: "no apply found for library" };
|
|
8390
8751
|
}
|
|
8391
|
-
const pkgPath =
|
|
8752
|
+
const pkgPath = import_node_path46.default.join(ctx.scanPath, "package.json");
|
|
8392
8753
|
if (await fileExists2(pkgPath)) {
|
|
8393
8754
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
8394
8755
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -8399,7 +8760,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8399
8760
|
}
|
|
8400
8761
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8401
8762
|
for (const file of hookFiles) {
|
|
8402
|
-
const filePath =
|
|
8763
|
+
const filePath = import_node_path46.default.join(ctx.scanPath, file);
|
|
8403
8764
|
const content = await import_node_fs28.promises.readFile(filePath, "utf8");
|
|
8404
8765
|
if (content.includes(match.registration_snippet)) {
|
|
8405
8766
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -8537,23 +8898,23 @@ function canonicalJson(value) {
|
|
|
8537
8898
|
|
|
8538
8899
|
// src/projects.ts
|
|
8539
8900
|
init_cjs_shims();
|
|
8540
|
-
var
|
|
8901
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
8541
8902
|
function pathsForProject(project, baseDir) {
|
|
8542
8903
|
if (project === DEFAULT_PROJECT) {
|
|
8543
8904
|
return {
|
|
8544
|
-
snapshotPath:
|
|
8545
|
-
errorsPath:
|
|
8546
|
-
staleEventsPath:
|
|
8547
|
-
embeddingsCachePath:
|
|
8548
|
-
policyViolationsPath:
|
|
8905
|
+
snapshotPath: import_node_path47.default.join(baseDir, "graph.json"),
|
|
8906
|
+
errorsPath: import_node_path47.default.join(baseDir, "errors.ndjson"),
|
|
8907
|
+
staleEventsPath: import_node_path47.default.join(baseDir, "stale-events.ndjson"),
|
|
8908
|
+
embeddingsCachePath: import_node_path47.default.join(baseDir, "embeddings.json"),
|
|
8909
|
+
policyViolationsPath: import_node_path47.default.join(baseDir, "policy-violations.ndjson")
|
|
8549
8910
|
};
|
|
8550
8911
|
}
|
|
8551
8912
|
return {
|
|
8552
|
-
snapshotPath:
|
|
8553
|
-
errorsPath:
|
|
8554
|
-
staleEventsPath:
|
|
8555
|
-
embeddingsCachePath:
|
|
8556
|
-
policyViolationsPath:
|
|
8913
|
+
snapshotPath: import_node_path47.default.join(baseDir, `${project}.json`),
|
|
8914
|
+
errorsPath: import_node_path47.default.join(baseDir, `errors.${project}.ndjson`),
|
|
8915
|
+
staleEventsPath: import_node_path47.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
8916
|
+
embeddingsCachePath: import_node_path47.default.join(baseDir, `embeddings.${project}.json`),
|
|
8917
|
+
policyViolationsPath: import_node_path47.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
8557
8918
|
};
|
|
8558
8919
|
}
|
|
8559
8920
|
var Projects = class {
|
|
@@ -8591,26 +8952,26 @@ var Projects = class {
|
|
|
8591
8952
|
init_cjs_shims();
|
|
8592
8953
|
var import_node_fs30 = require("fs");
|
|
8593
8954
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
8594
|
-
var
|
|
8595
|
-
var
|
|
8955
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
8956
|
+
var import_types34 = require("@neat.is/types");
|
|
8596
8957
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
8597
8958
|
var LOCK_RETRY_MS = 50;
|
|
8598
8959
|
function neatHome() {
|
|
8599
8960
|
const override = process.env.NEAT_HOME;
|
|
8600
|
-
if (override && override.length > 0) return
|
|
8601
|
-
return
|
|
8961
|
+
if (override && override.length > 0) return import_node_path48.default.resolve(override);
|
|
8962
|
+
return import_node_path48.default.join(import_node_os3.default.homedir(), ".neat");
|
|
8602
8963
|
}
|
|
8603
8964
|
function registryPath() {
|
|
8604
|
-
return
|
|
8965
|
+
return import_node_path48.default.join(neatHome(), "projects.json");
|
|
8605
8966
|
}
|
|
8606
8967
|
function registryLockPath() {
|
|
8607
|
-
return
|
|
8968
|
+
return import_node_path48.default.join(neatHome(), "projects.json.lock");
|
|
8608
8969
|
}
|
|
8609
8970
|
function daemonPidPath() {
|
|
8610
|
-
return
|
|
8971
|
+
return import_node_path48.default.join(neatHome(), "neatd.pid");
|
|
8611
8972
|
}
|
|
8612
8973
|
function daemonsDir() {
|
|
8613
|
-
return
|
|
8974
|
+
return import_node_path48.default.join(neatHome(), "daemons");
|
|
8614
8975
|
}
|
|
8615
8976
|
function isFiniteInt(v) {
|
|
8616
8977
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -8651,7 +9012,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
8651
9012
|
const out = [];
|
|
8652
9013
|
for (const name of names) {
|
|
8653
9014
|
if (!name.endsWith(".json")) continue;
|
|
8654
|
-
const file =
|
|
9015
|
+
const file = import_node_path48.default.join(dir, name);
|
|
8655
9016
|
let raw;
|
|
8656
9017
|
try {
|
|
8657
9018
|
raw = await import_node_fs30.promises.readFile(file, "utf8");
|
|
@@ -8772,7 +9133,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
8772
9133
|
}
|
|
8773
9134
|
}
|
|
8774
9135
|
async function normalizeProjectPath(input) {
|
|
8775
|
-
const resolved =
|
|
9136
|
+
const resolved = import_node_path48.default.resolve(input);
|
|
8776
9137
|
try {
|
|
8777
9138
|
return await import_node_fs30.promises.realpath(resolved);
|
|
8778
9139
|
} catch {
|
|
@@ -8780,7 +9141,7 @@ async function normalizeProjectPath(input) {
|
|
|
8780
9141
|
}
|
|
8781
9142
|
}
|
|
8782
9143
|
async function writeAtomically(target, contents) {
|
|
8783
|
-
await import_node_fs30.promises.mkdir(
|
|
9144
|
+
await import_node_fs30.promises.mkdir(import_node_path48.default.dirname(target), { recursive: true });
|
|
8784
9145
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
8785
9146
|
const fd = await import_node_fs30.promises.open(tmp, "w");
|
|
8786
9147
|
try {
|
|
@@ -8793,7 +9154,7 @@ async function writeAtomically(target, contents) {
|
|
|
8793
9154
|
}
|
|
8794
9155
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
8795
9156
|
const deadline = Date.now() + timeoutMs;
|
|
8796
|
-
await import_node_fs30.promises.mkdir(
|
|
9157
|
+
await import_node_fs30.promises.mkdir(import_node_path48.default.dirname(lockPath), { recursive: true });
|
|
8797
9158
|
let probedHolder = false;
|
|
8798
9159
|
while (true) {
|
|
8799
9160
|
try {
|
|
@@ -8846,10 +9207,10 @@ async function readRegistry() {
|
|
|
8846
9207
|
throw err;
|
|
8847
9208
|
}
|
|
8848
9209
|
const parsed = JSON.parse(raw);
|
|
8849
|
-
return
|
|
9210
|
+
return import_types34.RegistryFileSchema.parse(parsed);
|
|
8850
9211
|
}
|
|
8851
9212
|
async function writeRegistry(reg) {
|
|
8852
|
-
const validated =
|
|
9213
|
+
const validated = import_types34.RegistryFileSchema.parse(reg);
|
|
8853
9214
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
8854
9215
|
}
|
|
8855
9216
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -9035,7 +9396,7 @@ init_auth();
|
|
|
9035
9396
|
// src/connectors-config.ts
|
|
9036
9397
|
init_cjs_shims();
|
|
9037
9398
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
9038
|
-
var
|
|
9399
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
9039
9400
|
var import_node_fs31 = require("fs");
|
|
9040
9401
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
9041
9402
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -9050,11 +9411,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
9050
9411
|
};
|
|
9051
9412
|
function neatHome2() {
|
|
9052
9413
|
const override = process.env.NEAT_HOME;
|
|
9053
|
-
if (override && override.length > 0) return
|
|
9054
|
-
return
|
|
9414
|
+
if (override && override.length > 0) return import_node_path49.default.resolve(override);
|
|
9415
|
+
return import_node_path49.default.join(import_node_os4.default.homedir(), ".neat");
|
|
9055
9416
|
}
|
|
9056
9417
|
function connectorsConfigPath(home = neatHome2()) {
|
|
9057
|
-
return
|
|
9418
|
+
return import_node_path49.default.join(home, "connectors.json");
|
|
9058
9419
|
}
|
|
9059
9420
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
9060
9421
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -9185,7 +9546,7 @@ function connectorMatchesProject(entry2, project) {
|
|
|
9185
9546
|
var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
|
|
9186
9547
|
var CONNECTORS_LOCK_RETRY_MS = 50;
|
|
9187
9548
|
function connectorsConfigLockPath(home = neatHome2()) {
|
|
9188
|
-
return
|
|
9549
|
+
return import_node_path49.default.join(home, "connectors.json.lock");
|
|
9189
9550
|
}
|
|
9190
9551
|
function isEnvRef(value) {
|
|
9191
9552
|
return value.length > 1 && value.startsWith("$");
|
|
@@ -9198,7 +9559,7 @@ function redactCredentialRef(ref) {
|
|
|
9198
9559
|
return out;
|
|
9199
9560
|
}
|
|
9200
9561
|
async function writeConfigAtomically0600(file, contents) {
|
|
9201
|
-
await import_node_fs31.promises.mkdir(
|
|
9562
|
+
await import_node_fs31.promises.mkdir(import_node_path49.default.dirname(file), { recursive: true });
|
|
9202
9563
|
const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
9203
9564
|
const fd = await import_node_fs31.promises.open(tmp, "w", 384);
|
|
9204
9565
|
try {
|
|
@@ -9212,7 +9573,7 @@ async function writeConfigAtomically0600(file, contents) {
|
|
|
9212
9573
|
}
|
|
9213
9574
|
async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
|
|
9214
9575
|
const deadline = Date.now() + timeoutMs;
|
|
9215
|
-
await import_node_fs31.promises.mkdir(
|
|
9576
|
+
await import_node_fs31.promises.mkdir(import_node_path49.default.dirname(lockPath), { recursive: true });
|
|
9216
9577
|
for (; ; ) {
|
|
9217
9578
|
try {
|
|
9218
9579
|
const fd = await import_node_fs31.promises.open(lockPath, "wx");
|
|
@@ -9495,11 +9856,11 @@ function registerRoutes(scope, ctx) {
|
|
|
9495
9856
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
9496
9857
|
const parsed = [];
|
|
9497
9858
|
for (const c of candidates) {
|
|
9498
|
-
const r =
|
|
9859
|
+
const r = import_types35.DivergenceTypeSchema.safeParse(c);
|
|
9499
9860
|
if (!r.success) {
|
|
9500
9861
|
return reply.code(400).send({
|
|
9501
9862
|
error: `unknown divergence type "${c}"`,
|
|
9502
|
-
allowed:
|
|
9863
|
+
allowed: import_types35.DivergenceTypeSchema.options
|
|
9503
9864
|
});
|
|
9504
9865
|
}
|
|
9505
9866
|
parsed.push(r.data);
|
|
@@ -9770,7 +10131,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9770
10131
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
9771
10132
|
let violations = await log.readAll();
|
|
9772
10133
|
if (req.query.severity) {
|
|
9773
|
-
const sev =
|
|
10134
|
+
const sev = import_types35.PolicySeveritySchema.safeParse(req.query.severity);
|
|
9774
10135
|
if (!sev.success) {
|
|
9775
10136
|
return reply.code(400).send({
|
|
9776
10137
|
error: "invalid severity",
|
|
@@ -9809,7 +10170,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9809
10170
|
scope.post("/policies/check", async (req, reply) => {
|
|
9810
10171
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
9811
10172
|
if (!proj) return;
|
|
9812
|
-
const parsed =
|
|
10173
|
+
const parsed = import_types35.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
9813
10174
|
if (!parsed.success) {
|
|
9814
10175
|
return reply.code(400).send({
|
|
9815
10176
|
error: "invalid /policies/check body",
|
|
@@ -9943,7 +10304,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9943
10304
|
});
|
|
9944
10305
|
}
|
|
9945
10306
|
async function buildApi(opts) {
|
|
9946
|
-
const app = (0, import_fastify.default)({ logger: false });
|
|
10307
|
+
const app = (0, import_fastify.default)({ logger: false, routerOptions: { maxParamLength: 1024 } });
|
|
9947
10308
|
await app.register(import_cors.default, { origin: true });
|
|
9948
10309
|
const env = readAuthEnv();
|
|
9949
10310
|
const authToken = opts.authToken ?? env.authToken;
|
|
@@ -10075,13 +10436,13 @@ init_otel();
|
|
|
10075
10436
|
// src/daemon.ts
|
|
10076
10437
|
init_cjs_shims();
|
|
10077
10438
|
var import_node_fs33 = require("fs");
|
|
10078
|
-
var
|
|
10439
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
10079
10440
|
var import_node_module = require("module");
|
|
10080
10441
|
init_otel();
|
|
10081
10442
|
|
|
10082
10443
|
// src/connectors/index.ts
|
|
10083
10444
|
init_cjs_shims();
|
|
10084
|
-
var
|
|
10445
|
+
var import_types36 = require("@neat.is/types");
|
|
10085
10446
|
|
|
10086
10447
|
// src/connectors/registry.ts
|
|
10087
10448
|
init_cjs_shims();
|
|
@@ -10159,7 +10520,12 @@ var JUNCTION_DEFAULT_RATE_LIMITS = {
|
|
|
10159
10520
|
supabase: { capacity: 30, refillMs: 1e4 },
|
|
10160
10521
|
// Not a documented API limit at all — a self-imposed ceiling on the raw
|
|
10161
10522
|
// pg_stat_statements connection (see module header above).
|
|
10162
|
-
"supabase-postgres": { capacity: 20, refillMs: 3e3 }
|
|
10523
|
+
"supabase-postgres": { capacity: 20, refillMs: 3e3 },
|
|
10524
|
+
// Push provider (ADR-146): the Drains REST API is touched only by `neat
|
|
10525
|
+
// connector add/remove/test` (provision/deprovision/validate), never a poll
|
|
10526
|
+
// loop, so this bucket is exercised a handful of times per command. Kept
|
|
10527
|
+
// conservative pending a documented Drains-API rate limit.
|
|
10528
|
+
vercel: { capacity: 20, refillMs: 5e3 }
|
|
10163
10529
|
};
|
|
10164
10530
|
var JUNCTION_GENERIC_RATE_LIMIT = { capacity: 20, refillMs: 5e3 };
|
|
10165
10531
|
function defaultRateLimitFor(provider) {
|
|
@@ -10451,17 +10817,20 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
10451
10817
|
// src/connectors/supabase/map.ts
|
|
10452
10818
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
10453
10819
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
10454
|
-
function targetFromRestPath(
|
|
10455
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
10820
|
+
function targetFromRestPath(path63) {
|
|
10821
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path63);
|
|
10456
10822
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
10457
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
10823
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path63);
|
|
10458
10824
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
10459
10825
|
return null;
|
|
10460
10826
|
}
|
|
10461
10827
|
var ERROR_STATUS_THRESHOLD = 500;
|
|
10462
10828
|
function mapEdgeLogRowsToSignals(rows) {
|
|
10463
10829
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
10830
|
+
if (!Array.isArray(rows)) return [];
|
|
10464
10831
|
for (const row of rows) {
|
|
10832
|
+
if (!row || typeof row !== "object") continue;
|
|
10833
|
+
if (typeof row.path !== "string" || typeof row.timestamp !== "string") continue;
|
|
10465
10834
|
const target = targetFromRestPath(row.path);
|
|
10466
10835
|
if (!target) continue;
|
|
10467
10836
|
const key = `${target.targetKind}:${target.name}`;
|
|
@@ -10502,9 +10871,12 @@ function tableNameFromQueryText(query) {
|
|
|
10502
10871
|
function diffPgStatStatementsToSignals(rows, previous, nowIso2) {
|
|
10503
10872
|
const signals = [];
|
|
10504
10873
|
const seen = /* @__PURE__ */ new Set();
|
|
10874
|
+
if (!Array.isArray(rows)) return signals;
|
|
10505
10875
|
for (const row of rows) {
|
|
10506
|
-
|
|
10876
|
+
if (!row || typeof row !== "object" || typeof row.queryid !== "string") continue;
|
|
10507
10877
|
const calls = Number(row.calls);
|
|
10878
|
+
if (!Number.isFinite(calls)) continue;
|
|
10879
|
+
seen.add(row.queryid);
|
|
10508
10880
|
const prior = previous.get(row.queryid);
|
|
10509
10881
|
previous.set(row.queryid, { calls });
|
|
10510
10882
|
if (!prior || calls < prior.calls) continue;
|
|
@@ -10557,23 +10929,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
10557
10929
|
|
|
10558
10930
|
// src/connectors/supabase/resolve.ts
|
|
10559
10931
|
init_cjs_shims();
|
|
10560
|
-
var
|
|
10932
|
+
var import_types38 = require("@neat.is/types");
|
|
10561
10933
|
function createSupabaseResolveTarget(graph, config) {
|
|
10562
10934
|
return (signal, _ctx) => {
|
|
10563
10935
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
10564
10936
|
return null;
|
|
10565
10937
|
}
|
|
10566
|
-
const subResourceId = (0,
|
|
10938
|
+
const subResourceId = (0, import_types38.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
10567
10939
|
if (graph.hasNode(subResourceId)) {
|
|
10568
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
10940
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10569
10941
|
}
|
|
10570
|
-
const bareResourceId = (0,
|
|
10942
|
+
const bareResourceId = (0, import_types38.infraId)(signal.targetKind, signal.targetName);
|
|
10571
10943
|
if (graph.hasNode(bareResourceId)) {
|
|
10572
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
10944
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10573
10945
|
}
|
|
10574
|
-
const projectLevelId = (0,
|
|
10946
|
+
const projectLevelId = (0, import_types38.infraId)("supabase", config.nodeRef);
|
|
10575
10947
|
if (graph.hasNode(projectLevelId)) {
|
|
10576
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
10948
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10577
10949
|
}
|
|
10578
10950
|
return null;
|
|
10579
10951
|
};
|
|
@@ -10666,7 +11038,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
10666
11038
|
|
|
10667
11039
|
// src/connectors/railway/index.ts
|
|
10668
11040
|
init_cjs_shims();
|
|
10669
|
-
var
|
|
11041
|
+
var import_types42 = require("@neat.is/types");
|
|
10670
11042
|
|
|
10671
11043
|
// src/connectors/railway/client.ts
|
|
10672
11044
|
init_cjs_shims();
|
|
@@ -10792,7 +11164,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
10792
11164
|
const out = [];
|
|
10793
11165
|
graph.forEachNode((_id, attrs) => {
|
|
10794
11166
|
const node = attrs;
|
|
10795
|
-
if (node.type !==
|
|
11167
|
+
if (node.type !== import_types42.NodeType.RouteNode) return;
|
|
10796
11168
|
const route = attrs;
|
|
10797
11169
|
if (route.service !== serviceName) return;
|
|
10798
11170
|
out.push({
|
|
@@ -10828,7 +11200,12 @@ function upsertBucket(buckets2, key, isError, timestamp, build) {
|
|
|
10828
11200
|
}
|
|
10829
11201
|
function mapRailwayHttpLogsToSignals(entries, routeIndex) {
|
|
10830
11202
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
11203
|
+
if (!Array.isArray(entries)) return [];
|
|
10831
11204
|
for (const entry2 of entries) {
|
|
11205
|
+
if (!entry2 || typeof entry2 !== "object") continue;
|
|
11206
|
+
if (typeof entry2.method !== "string" || typeof entry2.path !== "string" || typeof entry2.timestamp !== "string") {
|
|
11207
|
+
continue;
|
|
11208
|
+
}
|
|
10832
11209
|
const method = entry2.method.toUpperCase();
|
|
10833
11210
|
const normalizedPath = normalizePathTemplate(entry2.path);
|
|
10834
11211
|
const match = findRailwayRoute(routeIndex, method, normalizedPath);
|
|
@@ -10867,8 +11244,11 @@ function mapRailwayHttpLogsToSignals(entries, routeIndex) {
|
|
|
10867
11244
|
}
|
|
10868
11245
|
function mapRailwayNetworkFlowLogsToSignals(entries) {
|
|
10869
11246
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
11247
|
+
if (!Array.isArray(entries)) return [];
|
|
10870
11248
|
for (const entry2 of entries) {
|
|
10871
|
-
if (!entry2
|
|
11249
|
+
if (!entry2 || typeof entry2 !== "object") continue;
|
|
11250
|
+
if (typeof entry2.peerServiceId !== "string" || entry2.peerServiceId.length === 0) continue;
|
|
11251
|
+
if (typeof entry2.timestamp !== "string") continue;
|
|
10872
11252
|
const isError = entry2.dropCause !== null && entry2.dropCause !== "";
|
|
10873
11253
|
upsertBucket(buckets2, entry2.peerServiceId, isError, entry2.timestamp, () => ({
|
|
10874
11254
|
targetKind: PEER_SERVICE_TARGET_KIND,
|
|
@@ -10888,12 +11268,12 @@ function createRailwayResolveTarget(config) {
|
|
|
10888
11268
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
10889
11269
|
if (!serviceName) return null;
|
|
10890
11270
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
10891
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
11271
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types42.EdgeType.CALLS };
|
|
10892
11272
|
}
|
|
10893
11273
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
10894
11274
|
const peerName = config.serviceNameById[signal.targetName];
|
|
10895
11275
|
if (!peerName) return null;
|
|
10896
|
-
return { targetNodeId: (0,
|
|
11276
|
+
return { targetNodeId: (0, import_types42.serviceId)(peerName), serviceName, edgeType: import_types42.EdgeType.CONNECTS_TO };
|
|
10897
11277
|
}
|
|
10898
11278
|
return null;
|
|
10899
11279
|
};
|
|
@@ -10996,7 +11376,7 @@ async function fetchHttpRequestLogEntries(creds, sinceIso) {
|
|
|
10996
11376
|
throw new Error(`Cloud Logging entries.list failed: ${res.status} ${res.statusText}`);
|
|
10997
11377
|
}
|
|
10998
11378
|
const json = await res.json();
|
|
10999
|
-
out.push(...json.entries
|
|
11379
|
+
if (Array.isArray(json.entries)) out.push(...json.entries);
|
|
11000
11380
|
if (!json.nextPageToken) break;
|
|
11001
11381
|
pageToken = json.nextPageToken;
|
|
11002
11382
|
}
|
|
@@ -11017,9 +11397,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
11017
11397
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
11018
11398
|
if (secondSep === -1) return null;
|
|
11019
11399
|
const method = rest.slice(0, secondSep);
|
|
11020
|
-
const
|
|
11021
|
-
if (!resourceName || !method || !
|
|
11022
|
-
return { resourceName, method, path:
|
|
11400
|
+
const path63 = rest.slice(secondSep + 1);
|
|
11401
|
+
if (!resourceName || !method || !path63) return null;
|
|
11402
|
+
return { resourceName, method, path: path63 };
|
|
11023
11403
|
}
|
|
11024
11404
|
function resourceNameFor(type, labels) {
|
|
11025
11405
|
if (!labels) return null;
|
|
@@ -11033,7 +11413,7 @@ function resourceNameFor(type, labels) {
|
|
|
11033
11413
|
}
|
|
11034
11414
|
}
|
|
11035
11415
|
function pathFromRequestUrl(requestUrl) {
|
|
11036
|
-
if (
|
|
11416
|
+
if (typeof requestUrl !== "string" || requestUrl.length === 0) return null;
|
|
11037
11417
|
if (requestUrl.startsWith("/")) {
|
|
11038
11418
|
const withoutQuery = requestUrl.split("?")[0];
|
|
11039
11419
|
return withoutQuery && withoutQuery.length > 0 ? withoutQuery : "/";
|
|
@@ -11048,22 +11428,23 @@ function pathFromRequestUrl(requestUrl) {
|
|
|
11048
11428
|
}
|
|
11049
11429
|
var ERROR_STATUS_THRESHOLD2 = 500;
|
|
11050
11430
|
function mapLogEntryToSignal(entry2) {
|
|
11431
|
+
if (!entry2 || typeof entry2 !== "object") return null;
|
|
11051
11432
|
const resourceType = entry2.resource?.type;
|
|
11052
11433
|
if (!resourceType || !isFirebaseResourceType(resourceType)) return null;
|
|
11053
11434
|
const resourceName = resourceNameFor(resourceType, entry2.resource?.labels);
|
|
11054
11435
|
if (!resourceName) return null;
|
|
11055
11436
|
const req = entry2.httpRequest;
|
|
11056
11437
|
if (!req) return null;
|
|
11057
|
-
if (
|
|
11438
|
+
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
11058
11439
|
const method = req.requestMethod.toUpperCase();
|
|
11059
|
-
const
|
|
11060
|
-
if (
|
|
11440
|
+
const path63 = pathFromRequestUrl(req.requestUrl);
|
|
11441
|
+
if (path63 === null) return null;
|
|
11061
11442
|
const timestamp = entry2.timestamp;
|
|
11062
|
-
if (
|
|
11443
|
+
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
11063
11444
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
11064
11445
|
return {
|
|
11065
11446
|
targetKind: resourceType,
|
|
11066
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
11447
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path63 }),
|
|
11067
11448
|
callCount: 1,
|
|
11068
11449
|
errorCount: isError ? 1 : 0,
|
|
11069
11450
|
lastObservedIso: timestamp
|
|
@@ -11080,7 +11461,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
11080
11461
|
|
|
11081
11462
|
// src/connectors/firebase/resolve.ts
|
|
11082
11463
|
init_cjs_shims();
|
|
11083
|
-
var
|
|
11464
|
+
var import_types43 = require("@neat.is/types");
|
|
11084
11465
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
11085
11466
|
switch (resourceType) {
|
|
11086
11467
|
case "cloud_function":
|
|
@@ -11095,7 +11476,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
11095
11476
|
const entries = [];
|
|
11096
11477
|
graph.forEachNode((_id, attrs) => {
|
|
11097
11478
|
const node = attrs;
|
|
11098
|
-
if (node.type !==
|
|
11479
|
+
if (node.type !== import_types43.NodeType.RouteNode) return;
|
|
11099
11480
|
const route = attrs;
|
|
11100
11481
|
if (route.service !== serviceName) return;
|
|
11101
11482
|
entries.push({
|
|
@@ -11127,7 +11508,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
11127
11508
|
return {
|
|
11128
11509
|
targetNodeId: match.routeNodeId,
|
|
11129
11510
|
serviceName,
|
|
11130
|
-
edgeType:
|
|
11511
|
+
edgeType: import_types43.EdgeType.CALLS
|
|
11131
11512
|
};
|
|
11132
11513
|
};
|
|
11133
11514
|
}
|
|
@@ -11154,7 +11535,7 @@ init_cjs_shims();
|
|
|
11154
11535
|
|
|
11155
11536
|
// src/connectors/cloudflare/connector.ts
|
|
11156
11537
|
init_cjs_shims();
|
|
11157
|
-
var
|
|
11538
|
+
var import_types45 = require("@neat.is/types");
|
|
11158
11539
|
|
|
11159
11540
|
// src/connectors/cloudflare/client.ts
|
|
11160
11541
|
init_cjs_shims();
|
|
@@ -11211,7 +11592,7 @@ async function queryWorkerInvocations(ctx, config, window, fetchImpl = fetch) {
|
|
|
11211
11592
|
throw new Error(`cloudflare connector: telemetry query returned an error (${message})`);
|
|
11212
11593
|
}
|
|
11213
11594
|
const events = payload.result?.events?.events;
|
|
11214
|
-
if (events
|
|
11595
|
+
if (!Array.isArray(events)) {
|
|
11215
11596
|
console.warn(
|
|
11216
11597
|
"[neat connector] cloudflare: telemetry query returned success:true but no result.events.events array \u2014 the response shape may have changed; treating as zero events this tick"
|
|
11217
11598
|
);
|
|
@@ -11241,7 +11622,7 @@ var HTTP_METHODS = /* @__PURE__ */ new Set([
|
|
|
11241
11622
|
]);
|
|
11242
11623
|
var LEADING_TOKEN_RE = /^(\S+)\s+\S/;
|
|
11243
11624
|
function parseHttpMethodFromTrigger(trigger) {
|
|
11244
|
-
if (
|
|
11625
|
+
if (typeof trigger !== "string") return null;
|
|
11245
11626
|
const match = LEADING_TOKEN_RE.exec(trigger.trim());
|
|
11246
11627
|
const token = match?.[1];
|
|
11247
11628
|
if (!token) return null;
|
|
@@ -11257,25 +11638,28 @@ function parsePathFromTrigger(trigger) {
|
|
|
11257
11638
|
}
|
|
11258
11639
|
var ERROR_STATUS_THRESHOLD3 = 500;
|
|
11259
11640
|
function mapEventToSignal(event) {
|
|
11641
|
+
if (!event || typeof event !== "object") return null;
|
|
11260
11642
|
const metadata = event.$metadata;
|
|
11261
11643
|
const workers = event.$workers;
|
|
11262
11644
|
const method = parseHttpMethodFromTrigger(metadata?.trigger);
|
|
11263
11645
|
if (!method) return null;
|
|
11264
11646
|
const scriptName = workers?.scriptName ?? metadata?.service;
|
|
11265
|
-
if (
|
|
11647
|
+
if (typeof scriptName !== "string" || scriptName.length === 0) return null;
|
|
11266
11648
|
const timestampMs = event.timestamp ?? metadata?.startTime;
|
|
11267
11649
|
if (typeof timestampMs !== "number" || !Number.isFinite(timestampMs)) return null;
|
|
11650
|
+
const observedAt = new Date(timestampMs);
|
|
11651
|
+
if (Number.isNaN(observedAt.getTime())) return null;
|
|
11268
11652
|
const statusCode = metadata?.statusCode;
|
|
11269
11653
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
11270
|
-
const
|
|
11654
|
+
const path63 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
11271
11655
|
return {
|
|
11272
11656
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
11273
11657
|
targetName: scriptName,
|
|
11274
11658
|
callCount: 1,
|
|
11275
11659
|
errorCount: isError ? 1 : 0,
|
|
11276
|
-
lastObservedIso:
|
|
11660
|
+
lastObservedIso: observedAt.toISOString(),
|
|
11277
11661
|
method,
|
|
11278
|
-
...
|
|
11662
|
+
...path63 ? { path: path63 } : {},
|
|
11279
11663
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
11280
11664
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
11281
11665
|
};
|
|
@@ -11315,19 +11699,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
11315
11699
|
graph.forEachNode((id, attrs) => {
|
|
11316
11700
|
if (found) return;
|
|
11317
11701
|
const a = attrs;
|
|
11318
|
-
if (a.type ===
|
|
11702
|
+
if (a.type === import_types45.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
11319
11703
|
found = id;
|
|
11320
11704
|
}
|
|
11321
11705
|
});
|
|
11322
11706
|
return found;
|
|
11323
11707
|
}
|
|
11324
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
11325
|
-
const normalizedPath = normalizePathTemplate(
|
|
11708
|
+
function findMatchingRouteNode(graph, serviceName, method, path63) {
|
|
11709
|
+
const normalizedPath = normalizePathTemplate(path63);
|
|
11326
11710
|
let found = null;
|
|
11327
11711
|
graph.forEachNode((id, attrs) => {
|
|
11328
11712
|
if (found) return;
|
|
11329
11713
|
const a = attrs;
|
|
11330
|
-
if (a.type !==
|
|
11714
|
+
if (a.type !== import_types45.NodeType.RouteNode || a.service !== serviceName) return;
|
|
11331
11715
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
11332
11716
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
11333
11717
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -11339,18 +11723,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11339
11723
|
return (signal) => {
|
|
11340
11724
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
11341
11725
|
const scriptName = signal.targetName;
|
|
11342
|
-
const { method, path:
|
|
11726
|
+
const { method, path: path63 } = signal;
|
|
11343
11727
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
11344
|
-
if (!method || !
|
|
11345
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
11728
|
+
if (!method || !path63) return wholeFileId;
|
|
11729
|
+
return findMatchingRouteNode(graph, serviceName, method, path63) ?? wholeFileId;
|
|
11346
11730
|
};
|
|
11347
11731
|
const mapping = config.workers?.[scriptName];
|
|
11348
11732
|
if (mapping) {
|
|
11349
|
-
const wholeFileId = (0,
|
|
11733
|
+
const wholeFileId = (0, import_types45.fileId)(mapping.service, mapping.entryFile);
|
|
11350
11734
|
return {
|
|
11351
11735
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
11352
11736
|
serviceName: mapping.service,
|
|
11353
|
-
edgeType:
|
|
11737
|
+
edgeType: import_types45.EdgeType.CALLS
|
|
11354
11738
|
};
|
|
11355
11739
|
}
|
|
11356
11740
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -11359,18 +11743,124 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11359
11743
|
return {
|
|
11360
11744
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
11361
11745
|
serviceName: fileNode.service,
|
|
11362
|
-
edgeType:
|
|
11746
|
+
edgeType: import_types45.EdgeType.CALLS
|
|
11363
11747
|
};
|
|
11364
11748
|
}
|
|
11365
11749
|
return {
|
|
11366
|
-
targetNodeId: (0,
|
|
11750
|
+
targetNodeId: (0, import_types45.infraId)("cloudflare-worker", scriptName),
|
|
11367
11751
|
serviceName: scriptName,
|
|
11368
|
-
edgeType:
|
|
11752
|
+
edgeType: import_types45.EdgeType.CALLS,
|
|
11369
11753
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
11370
11754
|
};
|
|
11371
11755
|
};
|
|
11372
11756
|
}
|
|
11373
11757
|
|
|
11758
|
+
// src/connectors/vercel/index.ts
|
|
11759
|
+
init_cjs_shims();
|
|
11760
|
+
|
|
11761
|
+
// src/connectors/vercel/client.ts
|
|
11762
|
+
init_cjs_shims();
|
|
11763
|
+
var DEFAULT_API_BASE_URL = "https://api.vercel.com";
|
|
11764
|
+
var DEFAULT_DRAIN_NAME = "neat-otlp";
|
|
11765
|
+
var TRACE_SCHEMAS = { trace: { version: "v1" } };
|
|
11766
|
+
function apiBase(config) {
|
|
11767
|
+
return config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
|
|
11768
|
+
}
|
|
11769
|
+
function teamQuery(config) {
|
|
11770
|
+
return `?teamId=${encodeURIComponent(config.teamId)}`;
|
|
11771
|
+
}
|
|
11772
|
+
function drainDelivery(config, otelToken) {
|
|
11773
|
+
return {
|
|
11774
|
+
type: "http",
|
|
11775
|
+
endpoint: config.endpoint,
|
|
11776
|
+
encoding: "json",
|
|
11777
|
+
headers: bearerAuthHeader(otelToken),
|
|
11778
|
+
...config.secret ? { secret: config.secret } : {}
|
|
11779
|
+
};
|
|
11780
|
+
}
|
|
11781
|
+
async function describeError(res) {
|
|
11782
|
+
try {
|
|
11783
|
+
const data = await res.json();
|
|
11784
|
+
const message = data?.error?.message;
|
|
11785
|
+
return typeof message === "string" && message.length > 0 ? ` \u2014 ${message}` : "";
|
|
11786
|
+
} catch {
|
|
11787
|
+
return "";
|
|
11788
|
+
}
|
|
11789
|
+
}
|
|
11790
|
+
async function createVercelDrain(config, credentials, fetchImpl = fetch) {
|
|
11791
|
+
const projectIds = config.projectIds ?? [];
|
|
11792
|
+
const body = {
|
|
11793
|
+
name: config.drainName ?? DEFAULT_DRAIN_NAME,
|
|
11794
|
+
projects: projectIds.length > 0 ? "some" : "all",
|
|
11795
|
+
...projectIds.length > 0 ? { projectIds } : {},
|
|
11796
|
+
schemas: TRACE_SCHEMAS,
|
|
11797
|
+
delivery: drainDelivery(config, credentials.otelToken),
|
|
11798
|
+
source: { kind: "self-served" }
|
|
11799
|
+
};
|
|
11800
|
+
const res = await junctionFetch(
|
|
11801
|
+
`${apiBase(config)}/v1/drains${teamQuery(config)}`,
|
|
11802
|
+
{
|
|
11803
|
+
method: "POST",
|
|
11804
|
+
headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
|
|
11805
|
+
body: JSON.stringify(body)
|
|
11806
|
+
},
|
|
11807
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11808
|
+
);
|
|
11809
|
+
if (!res.ok) {
|
|
11810
|
+
throw new Error(
|
|
11811
|
+
`vercel connector: create drain failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11812
|
+
);
|
|
11813
|
+
}
|
|
11814
|
+
const payload = await res.json().catch(() => null);
|
|
11815
|
+
if (!payload || typeof payload.id !== "string" || payload.id.length === 0) {
|
|
11816
|
+
throw new Error(
|
|
11817
|
+
"vercel connector: create drain returned no drain id \u2014 the Drains API response shape may have changed"
|
|
11818
|
+
);
|
|
11819
|
+
}
|
|
11820
|
+
return {
|
|
11821
|
+
id: payload.id,
|
|
11822
|
+
...payload.status ? { status: payload.status } : {},
|
|
11823
|
+
...payload.disabledReason ? { disabledReason: payload.disabledReason } : {}
|
|
11824
|
+
};
|
|
11825
|
+
}
|
|
11826
|
+
async function deleteVercelDrain(config, drainId, credentials, fetchImpl = fetch) {
|
|
11827
|
+
const res = await junctionFetch(
|
|
11828
|
+
`${apiBase(config)}/v1/drains/${encodeURIComponent(drainId)}${teamQuery(config)}`,
|
|
11829
|
+
{ method: "DELETE", headers: { ...bearerAuthHeader(credentials.token) } },
|
|
11830
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11831
|
+
);
|
|
11832
|
+
if (res.ok || res.status === 404) return;
|
|
11833
|
+
throw new Error(
|
|
11834
|
+
`vercel connector: delete drain failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11835
|
+
);
|
|
11836
|
+
}
|
|
11837
|
+
async function testVercelDrainDelivery(config, credentials, fetchImpl = fetch) {
|
|
11838
|
+
const res = await junctionFetch(
|
|
11839
|
+
`${apiBase(config)}/v1/drains/test${teamQuery(config)}`,
|
|
11840
|
+
{
|
|
11841
|
+
method: "POST",
|
|
11842
|
+
headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
|
|
11843
|
+
body: JSON.stringify({ schemas: TRACE_SCHEMAS, delivery: drainDelivery(config, credentials.otelToken) })
|
|
11844
|
+
},
|
|
11845
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11846
|
+
);
|
|
11847
|
+
if (res.status === 401 || res.status === 403) {
|
|
11848
|
+
return { status: "failure", error: `vercel rejected the API token (HTTP ${res.status})` };
|
|
11849
|
+
}
|
|
11850
|
+
if (!res.ok) {
|
|
11851
|
+
return {
|
|
11852
|
+
status: "failure",
|
|
11853
|
+
error: `vercel drain validation failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11854
|
+
};
|
|
11855
|
+
}
|
|
11856
|
+
const payload = await res.json().catch(() => null);
|
|
11857
|
+
return {
|
|
11858
|
+
...payload?.status ? { status: payload.status } : {},
|
|
11859
|
+
...payload?.error ? { error: payload.error } : {},
|
|
11860
|
+
...payload?.endpoint ? { endpoint: payload.endpoint } : {}
|
|
11861
|
+
};
|
|
11862
|
+
}
|
|
11863
|
+
|
|
11374
11864
|
// src/connectors/registry.ts
|
|
11375
11865
|
var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
|
|
11376
11866
|
async function authProbe(input) {
|
|
@@ -11522,8 +12012,96 @@ var PROVIDER_DISPATCH = {
|
|
|
11522
12012
|
}
|
|
11523
12013
|
}
|
|
11524
12014
|
};
|
|
11525
|
-
function
|
|
11526
|
-
return
|
|
12015
|
+
function vercelCredsFrom(credentials) {
|
|
12016
|
+
return { token: String(credentials.token ?? ""), otelToken: String(credentials.otelToken ?? "") };
|
|
12017
|
+
}
|
|
12018
|
+
function vercelConfigFromOptions(options) {
|
|
12019
|
+
const raw = options.projectIds;
|
|
12020
|
+
let projectIds;
|
|
12021
|
+
if (Array.isArray(raw)) projectIds = raw.filter((p) => typeof p === "string");
|
|
12022
|
+
else if (typeof raw === "string" && raw.trim().length > 0) {
|
|
12023
|
+
projectIds = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
12024
|
+
}
|
|
12025
|
+
return {
|
|
12026
|
+
teamId: String(options.teamId ?? ""),
|
|
12027
|
+
endpoint: String(options.endpoint ?? ""),
|
|
12028
|
+
...projectIds && projectIds.length > 0 ? { projectIds } : {},
|
|
12029
|
+
...typeof options.drainId === "string" ? { drainId: options.drainId } : {},
|
|
12030
|
+
...typeof options.drainName === "string" ? { drainName: options.drainName } : {},
|
|
12031
|
+
...typeof options.apiBaseUrl === "string" ? { apiBaseUrl: options.apiBaseUrl } : {},
|
|
12032
|
+
...typeof options.secret === "string" ? { secret: options.secret } : {}
|
|
12033
|
+
};
|
|
12034
|
+
}
|
|
12035
|
+
var PUSH_PROVIDER_DISPATCH = {
|
|
12036
|
+
vercel: {
|
|
12037
|
+
provider: "vercel",
|
|
12038
|
+
// The Vercel access token is the "primary" secret a single `--token`
|
|
12039
|
+
// populates; `otelToken` (the daemon's OTLP bearer) is the second field.
|
|
12040
|
+
primaryCredentialKey: "token",
|
|
12041
|
+
requiredCredentialFields: ["token", "otelToken"],
|
|
12042
|
+
// teamId scopes every Drains call; endpoint is where the drain delivers.
|
|
12043
|
+
// projectIds is optional (absent → the drain covers the whole team).
|
|
12044
|
+
requiredOptionFields: ["teamId", "endpoint"],
|
|
12045
|
+
// POST /v1/drains/test — authenticates the token and pings the endpoint
|
|
12046
|
+
// with a sample event, so `success` means the credential is live *and* the
|
|
12047
|
+
// daemon's OTLP endpoint is reachable and accepted the drain's bearer.
|
|
12048
|
+
async validate({ credentials, options, fetchImpl }) {
|
|
12049
|
+
const result = await testVercelDrainDelivery(
|
|
12050
|
+
vercelConfigFromOptions(options),
|
|
12051
|
+
vercelCredsFrom(credentials),
|
|
12052
|
+
fetchImpl
|
|
12053
|
+
);
|
|
12054
|
+
if (result.status === "success") return { ok: true };
|
|
12055
|
+
return {
|
|
12056
|
+
ok: false,
|
|
12057
|
+
reason: result.error ?? `vercel drain delivery test returned "${result.status ?? "no status"}"`
|
|
12058
|
+
};
|
|
12059
|
+
},
|
|
12060
|
+
// POST /v1/drains — creates the trace drain, returns its id to store in
|
|
12061
|
+
// `options.drainId`. A created-but-not-enabled drain is surfaced as a note,
|
|
12062
|
+
// not a failure (the entry still points at a real drain).
|
|
12063
|
+
async provision({ credentials, options, fetchImpl }) {
|
|
12064
|
+
try {
|
|
12065
|
+
const created = await createVercelDrain(
|
|
12066
|
+
vercelConfigFromOptions(options),
|
|
12067
|
+
vercelCredsFrom(credentials),
|
|
12068
|
+
fetchImpl
|
|
12069
|
+
);
|
|
12070
|
+
const note = created.status && created.status !== "enabled" ? `the drain was created but its status is "${created.status}"${created.disabledReason ? ` (${created.disabledReason})` : ""} \u2014 check the Vercel dashboard` : void 0;
|
|
12071
|
+
return { ok: true, options: { drainId: created.id }, ...note ? { note } : {} };
|
|
12072
|
+
} catch (err) {
|
|
12073
|
+
return { ok: false, reason: err.message };
|
|
12074
|
+
}
|
|
12075
|
+
},
|
|
12076
|
+
// DELETE /v1/drains/{id} — idempotent (deleteVercelDrain treats 404 as
|
|
12077
|
+
// success). No recorded drainId → nothing to delete, still a success.
|
|
12078
|
+
async deprovision({ credentials, options, fetchImpl }) {
|
|
12079
|
+
const drainId = typeof options.drainId === "string" ? options.drainId : "";
|
|
12080
|
+
if (!drainId) {
|
|
12081
|
+
return { ok: true, note: "no drain id was recorded \u2014 nothing to delete on the Vercel side" };
|
|
12082
|
+
}
|
|
12083
|
+
try {
|
|
12084
|
+
await deleteVercelDrain(
|
|
12085
|
+
vercelConfigFromOptions(options),
|
|
12086
|
+
drainId,
|
|
12087
|
+
vercelCredsFrom(credentials),
|
|
12088
|
+
fetchImpl
|
|
12089
|
+
);
|
|
12090
|
+
return { ok: true };
|
|
12091
|
+
} catch (err) {
|
|
12092
|
+
return { ok: false, reason: err.message };
|
|
12093
|
+
}
|
|
12094
|
+
}
|
|
12095
|
+
}
|
|
12096
|
+
};
|
|
12097
|
+
function isPushProvider(provider) {
|
|
12098
|
+
return provider in PUSH_PROVIDER_DISPATCH;
|
|
12099
|
+
}
|
|
12100
|
+
function getProviderFieldSchema(provider) {
|
|
12101
|
+
return PROVIDER_DISPATCH[provider] ?? PUSH_PROVIDER_DISPATCH[provider];
|
|
12102
|
+
}
|
|
12103
|
+
function knownProviderNames() {
|
|
12104
|
+
return [...Object.keys(PROVIDER_DISPATCH), ...Object.keys(PUSH_PROVIDER_DISPATCH)].sort();
|
|
11527
12105
|
}
|
|
11528
12106
|
function resolveEntryCredentials(dispatch, entry2, env) {
|
|
11529
12107
|
let credentials;
|
|
@@ -11545,7 +12123,7 @@ function resolveEntryCredentials(dispatch, entry2, env) {
|
|
|
11545
12123
|
return { ok: true, credentials };
|
|
11546
12124
|
}
|
|
11547
12125
|
async function validateConnectorEntry(entry2, env = process.env, fetchImpl) {
|
|
11548
|
-
const dispatch = PROVIDER_DISPATCH[entry2.provider];
|
|
12126
|
+
const dispatch = PROVIDER_DISPATCH[entry2.provider] ?? PUSH_PROVIDER_DISPATCH[entry2.provider];
|
|
11549
12127
|
if (!dispatch) {
|
|
11550
12128
|
return { status: "unknown-provider", reason: `unknown provider "${entry2.provider}"` };
|
|
11551
12129
|
}
|
|
@@ -11569,6 +12147,54 @@ async function validateConnectorEntry(entry2, env = process.env, fetchImpl) {
|
|
|
11569
12147
|
});
|
|
11570
12148
|
return result.ok ? { status: "ok" } : { status: "rejected", reason: result.reason };
|
|
11571
12149
|
}
|
|
12150
|
+
function resolvePushEntry(entry2, env) {
|
|
12151
|
+
const dispatch = PUSH_PROVIDER_DISPATCH[entry2.provider];
|
|
12152
|
+
if (!dispatch) {
|
|
12153
|
+
return PROVIDER_DISPATCH[entry2.provider] ? {
|
|
12154
|
+
ok: false,
|
|
12155
|
+
outcome: {
|
|
12156
|
+
status: "not-push",
|
|
12157
|
+
reason: `provider "${entry2.provider}" is polled, not provisioned \u2014 there is no drain to manage`
|
|
12158
|
+
}
|
|
12159
|
+
} : { ok: false, outcome: { status: "unknown-provider", reason: `unknown provider "${entry2.provider}"` } };
|
|
12160
|
+
}
|
|
12161
|
+
const creds = resolveEntryCredentials(dispatch, entry2, env);
|
|
12162
|
+
if (!creds.ok) {
|
|
12163
|
+
const status2 = creds.kind === "unset-env" ? "unset-env" : creds.kind === "missing-field" ? "missing-field" : "failed";
|
|
12164
|
+
return { ok: false, outcome: { status: status2, reason: creds.reason } };
|
|
12165
|
+
}
|
|
12166
|
+
const options = entry2.options ?? {};
|
|
12167
|
+
const missingOpts = dispatch.requiredOptionFields.filter((k) => !(k in options));
|
|
12168
|
+
if (missingOpts.length > 0) {
|
|
12169
|
+
return {
|
|
12170
|
+
ok: false,
|
|
12171
|
+
outcome: { status: "missing-field", reason: `options missing required field(s): ${missingOpts.join(", ")}` }
|
|
12172
|
+
};
|
|
12173
|
+
}
|
|
12174
|
+
return { ok: true, dispatch, credentials: creds.credentials, options };
|
|
12175
|
+
}
|
|
12176
|
+
async function provisionConnector(entry2, env = process.env, fetchImpl) {
|
|
12177
|
+
const resolved = resolvePushEntry(entry2, env);
|
|
12178
|
+
if (!resolved.ok) return resolved.outcome;
|
|
12179
|
+
const result = await resolved.dispatch.provision({
|
|
12180
|
+
credentials: resolved.credentials,
|
|
12181
|
+
options: resolved.options,
|
|
12182
|
+
...fetchImpl ? { fetchImpl } : {}
|
|
12183
|
+
});
|
|
12184
|
+
if (!result.ok) return { status: "failed", reason: result.reason };
|
|
12185
|
+
return { status: "ok", ...result.options ? { options: result.options } : {}, ...result.note ? { note: result.note } : {} };
|
|
12186
|
+
}
|
|
12187
|
+
async function deprovisionConnector(entry2, env = process.env, fetchImpl) {
|
|
12188
|
+
const resolved = resolvePushEntry(entry2, env);
|
|
12189
|
+
if (!resolved.ok) return resolved.outcome;
|
|
12190
|
+
const result = await resolved.dispatch.deprovision({
|
|
12191
|
+
credentials: resolved.credentials,
|
|
12192
|
+
options: resolved.options,
|
|
12193
|
+
...fetchImpl ? { fetchImpl } : {}
|
|
12194
|
+
});
|
|
12195
|
+
if (!result.ok) return { status: "failed", reason: result.reason };
|
|
12196
|
+
return { status: "ok", ...result.note ? { note: result.note } : {} };
|
|
12197
|
+
}
|
|
11572
12198
|
|
|
11573
12199
|
// src/daemon.ts
|
|
11574
12200
|
init_auth();
|
|
@@ -11576,28 +12202,28 @@ init_auth();
|
|
|
11576
12202
|
// src/unrouted.ts
|
|
11577
12203
|
init_cjs_shims();
|
|
11578
12204
|
var import_node_fs32 = require("fs");
|
|
11579
|
-
var
|
|
12205
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
11580
12206
|
|
|
11581
12207
|
// src/daemon.ts
|
|
11582
|
-
var
|
|
12208
|
+
var import_types48 = require("@neat.is/types");
|
|
11583
12209
|
function daemonJsonPath(scanPath) {
|
|
11584
|
-
return
|
|
12210
|
+
return import_node_path53.default.join(scanPath, "neat-out", "daemon.json");
|
|
11585
12211
|
}
|
|
11586
12212
|
function daemonsDiscoveryDir(home) {
|
|
11587
12213
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
11588
|
-
return
|
|
12214
|
+
return import_node_path53.default.join(base, "daemons");
|
|
11589
12215
|
}
|
|
11590
12216
|
function daemonDiscoveryPath(project, home) {
|
|
11591
|
-
return
|
|
12217
|
+
return import_node_path53.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
11592
12218
|
}
|
|
11593
12219
|
function sanitizeDiscoveryName(project) {
|
|
11594
12220
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
11595
12221
|
}
|
|
11596
12222
|
function neatHomeFromEnv() {
|
|
11597
12223
|
const env = process.env.NEAT_HOME;
|
|
11598
|
-
if (env && env.length > 0) return
|
|
12224
|
+
if (env && env.length > 0) return import_node_path53.default.resolve(env);
|
|
11599
12225
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11600
|
-
return
|
|
12226
|
+
return import_node_path53.default.join(home, ".neat");
|
|
11601
12227
|
}
|
|
11602
12228
|
async function readDaemonRecord(scanPath) {
|
|
11603
12229
|
try {
|
|
@@ -11668,7 +12294,7 @@ init_otel_grpc();
|
|
|
11668
12294
|
// src/search.ts
|
|
11669
12295
|
init_cjs_shims();
|
|
11670
12296
|
var import_node_fs34 = require("fs");
|
|
11671
|
-
var
|
|
12297
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
11672
12298
|
var import_node_crypto4 = require("crypto");
|
|
11673
12299
|
var DEFAULT_LIMIT = 10;
|
|
11674
12300
|
var NOMIC_DIM = 768;
|
|
@@ -11831,7 +12457,7 @@ async function readCache(cachePath) {
|
|
|
11831
12457
|
}
|
|
11832
12458
|
}
|
|
11833
12459
|
async function writeCache(cachePath, cache) {
|
|
11834
|
-
await import_node_fs34.promises.mkdir(
|
|
12460
|
+
await import_node_fs34.promises.mkdir(import_node_path54.default.dirname(cachePath), { recursive: true });
|
|
11835
12461
|
await import_node_fs34.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
11836
12462
|
}
|
|
11837
12463
|
var VectorIndex = class {
|
|
@@ -11989,8 +12615,8 @@ var ALL_PHASES = [
|
|
|
11989
12615
|
];
|
|
11990
12616
|
function classifyChange(relPath) {
|
|
11991
12617
|
const phases = /* @__PURE__ */ new Set();
|
|
11992
|
-
const base =
|
|
11993
|
-
const segments = relPath.split(
|
|
12618
|
+
const base = import_node_path55.default.basename(relPath).toLowerCase();
|
|
12619
|
+
const segments = relPath.split(import_node_path55.default.sep).map((s) => s.toLowerCase());
|
|
11994
12620
|
if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
|
|
11995
12621
|
phases.add("services");
|
|
11996
12622
|
phases.add("aliases");
|
|
@@ -12118,9 +12744,9 @@ function countWatchableDirs(scanPath, limit) {
|
|
|
12118
12744
|
for (const e of entries) {
|
|
12119
12745
|
if (count >= limit) return;
|
|
12120
12746
|
if (!e.isDirectory()) continue;
|
|
12121
|
-
if (IGNORED_WATCH_PATHS.some((re) => re.test(
|
|
12747
|
+
if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path55.default.join(dir, e.name) + import_node_path55.default.sep))) continue;
|
|
12122
12748
|
count++;
|
|
12123
|
-
if (depth < 2) visit(
|
|
12749
|
+
if (depth < 2) visit(import_node_path55.default.join(dir, e.name), depth + 1);
|
|
12124
12750
|
}
|
|
12125
12751
|
};
|
|
12126
12752
|
visit(scanPath, 0);
|
|
@@ -12138,8 +12764,8 @@ async function startWatch(graph, opts) {
|
|
|
12138
12764
|
const projectName = opts.project ?? DEFAULT_PROJECT;
|
|
12139
12765
|
await loadGraphFromDisk(graph, opts.outPath);
|
|
12140
12766
|
const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
|
|
12141
|
-
const policyFilePath =
|
|
12142
|
-
const policyViolationsPath =
|
|
12767
|
+
const policyFilePath = import_node_path55.default.join(opts.scanPath, "policy.json");
|
|
12768
|
+
const policyViolationsPath = import_node_path55.default.join(import_node_path55.default.dirname(opts.outPath), "policy-violations.ndjson");
|
|
12143
12769
|
let policies = [];
|
|
12144
12770
|
try {
|
|
12145
12771
|
policies = await loadPolicyFile(policyFilePath);
|
|
@@ -12190,7 +12816,7 @@ async function startWatch(graph, opts) {
|
|
|
12190
12816
|
assertBindAuthority(host, auth.authToken);
|
|
12191
12817
|
const port = opts.port ?? 8080;
|
|
12192
12818
|
const otelPort = opts.otelPort ?? 4318;
|
|
12193
|
-
const cachePath = opts.embeddingsCachePath ??
|
|
12819
|
+
const cachePath = opts.embeddingsCachePath ?? import_node_path55.default.join(import_node_path55.default.dirname(opts.outPath), "embeddings.json");
|
|
12194
12820
|
let searchIndex;
|
|
12195
12821
|
try {
|
|
12196
12822
|
searchIndex = await buildSearchIndex(graph, { cachePath });
|
|
@@ -12208,7 +12834,7 @@ async function startWatch(graph, opts) {
|
|
|
12208
12834
|
// Paths are derived from the explicit options the watch caller passes
|
|
12209
12835
|
// — pathsForProject is only used to fill in the embeddings/snapshot
|
|
12210
12836
|
// fields so the registry shape is complete.
|
|
12211
|
-
...pathsForProject(projectName,
|
|
12837
|
+
...pathsForProject(projectName, import_node_path55.default.dirname(opts.outPath)),
|
|
12212
12838
|
snapshotPath: opts.outPath,
|
|
12213
12839
|
errorsPath: opts.errorsPath,
|
|
12214
12840
|
staleEventsPath: opts.staleEventsPath
|
|
@@ -12326,9 +12952,9 @@ async function startWatch(graph, opts) {
|
|
|
12326
12952
|
};
|
|
12327
12953
|
const onPath = (absPath) => {
|
|
12328
12954
|
if (shouldIgnore(absPath)) return;
|
|
12329
|
-
const rel =
|
|
12955
|
+
const rel = import_node_path55.default.relative(opts.scanPath, absPath);
|
|
12330
12956
|
if (!rel || rel.startsWith("..")) return;
|
|
12331
|
-
pendingPaths.add(rel.split(
|
|
12957
|
+
pendingPaths.add(rel.split(import_node_path55.default.sep).join("/"));
|
|
12332
12958
|
const phases = classifyChange(rel);
|
|
12333
12959
|
if (phases.size === 0) {
|
|
12334
12960
|
for (const p of ALL_PHASES) pending.add(p);
|
|
@@ -12385,7 +13011,7 @@ async function startWatch(graph, opts) {
|
|
|
12385
13011
|
// src/deploy/detect.ts
|
|
12386
13012
|
init_cjs_shims();
|
|
12387
13013
|
var import_node_fs36 = require("fs");
|
|
12388
|
-
var
|
|
13014
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
12389
13015
|
var import_node_child_process2 = require("child_process");
|
|
12390
13016
|
var import_node_crypto5 = require("crypto");
|
|
12391
13017
|
function generateToken() {
|
|
@@ -12485,7 +13111,7 @@ async function runDeploy(opts = {}) {
|
|
|
12485
13111
|
const token = generateToken();
|
|
12486
13112
|
switch (substrate) {
|
|
12487
13113
|
case "docker-compose": {
|
|
12488
|
-
const artifactPath =
|
|
13114
|
+
const artifactPath = import_node_path56.default.join(cwd, "docker-compose.neat.yml");
|
|
12489
13115
|
const contents = emitDockerCompose(cwd);
|
|
12490
13116
|
await import_node_fs36.promises.writeFile(artifactPath, contents, "utf8");
|
|
12491
13117
|
return {
|
|
@@ -12493,11 +13119,11 @@ async function runDeploy(opts = {}) {
|
|
|
12493
13119
|
artifactPath,
|
|
12494
13120
|
token,
|
|
12495
13121
|
contents,
|
|
12496
|
-
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${
|
|
13122
|
+
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path56.default.basename(artifactPath)} up -d`
|
|
12497
13123
|
};
|
|
12498
13124
|
}
|
|
12499
13125
|
case "systemd": {
|
|
12500
|
-
const artifactPath =
|
|
13126
|
+
const artifactPath = import_node_path56.default.join(cwd, "neat.service");
|
|
12501
13127
|
const contents = emitSystemdUnit(cwd);
|
|
12502
13128
|
await import_node_fs36.promises.writeFile(artifactPath, contents, "utf8");
|
|
12503
13129
|
return {
|
|
@@ -12533,7 +13159,7 @@ init_cjs_shims();
|
|
|
12533
13159
|
// src/installers/javascript.ts
|
|
12534
13160
|
init_cjs_shims();
|
|
12535
13161
|
var import_node_fs37 = require("fs");
|
|
12536
|
-
var
|
|
13162
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
12537
13163
|
var import_semver2 = __toESM(require("semver"), 1);
|
|
12538
13164
|
|
|
12539
13165
|
// src/installers/templates.ts
|
|
@@ -13164,11 +13790,11 @@ var OTEL_ENV = {
|
|
|
13164
13790
|
value: "http://localhost:4318/projects/<project>/v1/traces"
|
|
13165
13791
|
};
|
|
13166
13792
|
function serviceNodeName(pkg, serviceDir) {
|
|
13167
|
-
return pkg.name ??
|
|
13793
|
+
return pkg.name ?? import_node_path57.default.basename(serviceDir);
|
|
13168
13794
|
}
|
|
13169
13795
|
function projectToken(pkg, serviceDir, project) {
|
|
13170
13796
|
if (project && project.length > 0) return project;
|
|
13171
|
-
return pkg.name ??
|
|
13797
|
+
return pkg.name ?? import_node_path57.default.basename(serviceDir);
|
|
13172
13798
|
}
|
|
13173
13799
|
async function readJsonFile(p) {
|
|
13174
13800
|
try {
|
|
@@ -13181,16 +13807,16 @@ async function readJsonFile(p) {
|
|
|
13181
13807
|
async function detectRuntimeKind(pkgRoot, pkg) {
|
|
13182
13808
|
const deps = allDeps(pkg);
|
|
13183
13809
|
if ("react-native" in deps || "expo" in deps) return "react-native";
|
|
13184
|
-
const appJson = await readJsonFile(
|
|
13810
|
+
const appJson = await readJsonFile(import_node_path57.default.join(pkgRoot, "app.json"));
|
|
13185
13811
|
if (appJson && typeof appJson === "object" && "expo" in appJson) {
|
|
13186
13812
|
return "react-native";
|
|
13187
13813
|
}
|
|
13188
|
-
if (await exists3(
|
|
13814
|
+
if (await exists3(import_node_path57.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path57.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path57.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
|
|
13189
13815
|
return "browser-bundle";
|
|
13190
13816
|
}
|
|
13191
|
-
if (await exists3(
|
|
13192
|
-
if (await exists3(
|
|
13193
|
-
if (await exists3(
|
|
13817
|
+
if (await exists3(import_node_path57.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
|
|
13818
|
+
if (await exists3(import_node_path57.default.join(pkgRoot, "bun.lockb"))) return "bun";
|
|
13819
|
+
if (await exists3(import_node_path57.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path57.default.join(pkgRoot, "deno.lock"))) {
|
|
13194
13820
|
return "deno";
|
|
13195
13821
|
}
|
|
13196
13822
|
const engines = pkg.engines ?? {};
|
|
@@ -13199,7 +13825,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
|
|
|
13199
13825
|
}
|
|
13200
13826
|
async function readPackageJson2(serviceDir) {
|
|
13201
13827
|
try {
|
|
13202
|
-
const raw = await import_node_fs37.promises.readFile(
|
|
13828
|
+
const raw = await import_node_fs37.promises.readFile(import_node_path57.default.join(serviceDir, "package.json"), "utf8");
|
|
13203
13829
|
return JSON.parse(raw);
|
|
13204
13830
|
} catch {
|
|
13205
13831
|
return null;
|
|
@@ -13243,7 +13869,7 @@ function needsVersionUpgrade(installed, expected) {
|
|
|
13243
13869
|
var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
|
|
13244
13870
|
async function findNextConfig(serviceDir) {
|
|
13245
13871
|
for (const name of NEXT_CONFIG_CANDIDATES) {
|
|
13246
|
-
const candidate =
|
|
13872
|
+
const candidate = import_node_path57.default.join(serviceDir, name);
|
|
13247
13873
|
if (await exists3(candidate)) return candidate;
|
|
13248
13874
|
}
|
|
13249
13875
|
return null;
|
|
@@ -13312,7 +13938,7 @@ function hasRemixDependency(pkg) {
|
|
|
13312
13938
|
}
|
|
13313
13939
|
async function findRemixEntry(serviceDir) {
|
|
13314
13940
|
for (const rel of REMIX_ENTRY_CANDIDATES) {
|
|
13315
|
-
const candidate =
|
|
13941
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13316
13942
|
if (await exists3(candidate)) return candidate;
|
|
13317
13943
|
}
|
|
13318
13944
|
return null;
|
|
@@ -13324,14 +13950,14 @@ function hasSvelteKitDependency(pkg) {
|
|
|
13324
13950
|
}
|
|
13325
13951
|
async function findSvelteKitHooks(serviceDir) {
|
|
13326
13952
|
for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
|
|
13327
|
-
const candidate =
|
|
13953
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13328
13954
|
if (await exists3(candidate)) return candidate;
|
|
13329
13955
|
}
|
|
13330
13956
|
return null;
|
|
13331
13957
|
}
|
|
13332
13958
|
async function findSvelteKitConfig(serviceDir) {
|
|
13333
13959
|
for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
|
|
13334
|
-
const candidate =
|
|
13960
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13335
13961
|
if (await exists3(candidate)) return candidate;
|
|
13336
13962
|
}
|
|
13337
13963
|
return null;
|
|
@@ -13342,7 +13968,7 @@ function hasNuxtDependency(pkg) {
|
|
|
13342
13968
|
}
|
|
13343
13969
|
async function findNuxtConfig(serviceDir) {
|
|
13344
13970
|
for (const name of NUXT_CONFIG_CANDIDATES) {
|
|
13345
|
-
const candidate =
|
|
13971
|
+
const candidate = import_node_path57.default.join(serviceDir, name);
|
|
13346
13972
|
if (await exists3(candidate)) return candidate;
|
|
13347
13973
|
}
|
|
13348
13974
|
return null;
|
|
@@ -13353,7 +13979,7 @@ function hasAstroDependency(pkg) {
|
|
|
13353
13979
|
}
|
|
13354
13980
|
async function findAstroConfig(serviceDir) {
|
|
13355
13981
|
for (const name of ASTRO_CONFIG_CANDIDATES) {
|
|
13356
|
-
const candidate =
|
|
13982
|
+
const candidate = import_node_path57.default.join(serviceDir, name);
|
|
13357
13983
|
if (await exists3(candidate)) return candidate;
|
|
13358
13984
|
}
|
|
13359
13985
|
return null;
|
|
@@ -13367,7 +13993,7 @@ function parseNextMajor(range) {
|
|
|
13367
13993
|
return Number.isFinite(n) ? n : null;
|
|
13368
13994
|
}
|
|
13369
13995
|
async function isTypeScriptProject(serviceDir) {
|
|
13370
|
-
return exists3(
|
|
13996
|
+
return exists3(import_node_path57.default.join(serviceDir, "tsconfig.json"));
|
|
13371
13997
|
}
|
|
13372
13998
|
var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
|
|
13373
13999
|
var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
|
|
@@ -13416,7 +14042,7 @@ function entryFromScript(script) {
|
|
|
13416
14042
|
}
|
|
13417
14043
|
async function resolveEntry(serviceDir, pkg) {
|
|
13418
14044
|
if (typeof pkg.main === "string" && pkg.main.length > 0) {
|
|
13419
|
-
const candidate =
|
|
14045
|
+
const candidate = import_node_path57.default.resolve(serviceDir, pkg.main);
|
|
13420
14046
|
if (await exists3(candidate)) return candidate;
|
|
13421
14047
|
}
|
|
13422
14048
|
if (pkg.bin) {
|
|
@@ -13430,40 +14056,40 @@ async function resolveEntry(serviceDir, pkg) {
|
|
|
13430
14056
|
if (typeof first === "string") binEntry = first;
|
|
13431
14057
|
}
|
|
13432
14058
|
if (binEntry) {
|
|
13433
|
-
const candidate =
|
|
14059
|
+
const candidate = import_node_path57.default.resolve(serviceDir, binEntry);
|
|
13434
14060
|
if (await exists3(candidate)) return candidate;
|
|
13435
14061
|
}
|
|
13436
14062
|
}
|
|
13437
14063
|
const startEntry = entryFromScript(pkg.scripts?.start);
|
|
13438
14064
|
if (startEntry) {
|
|
13439
|
-
const candidate =
|
|
14065
|
+
const candidate = import_node_path57.default.resolve(serviceDir, startEntry);
|
|
13440
14066
|
if (await exists3(candidate)) return candidate;
|
|
13441
14067
|
}
|
|
13442
14068
|
const devEntry = entryFromScript(pkg.scripts?.dev);
|
|
13443
14069
|
if (devEntry) {
|
|
13444
|
-
const candidate =
|
|
14070
|
+
const candidate = import_node_path57.default.resolve(serviceDir, devEntry);
|
|
13445
14071
|
if (await exists3(candidate)) return candidate;
|
|
13446
14072
|
}
|
|
13447
14073
|
for (const rel of SRC_INDEX_CANDIDATES) {
|
|
13448
|
-
const candidate =
|
|
14074
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13449
14075
|
if (await exists3(candidate)) return candidate;
|
|
13450
14076
|
}
|
|
13451
14077
|
for (const rel of SRC_NAMED_CANDIDATES) {
|
|
13452
|
-
const candidate =
|
|
14078
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13453
14079
|
if (await exists3(candidate)) return candidate;
|
|
13454
14080
|
}
|
|
13455
14081
|
for (const rel of ROOT_NAMED_CANDIDATES) {
|
|
13456
|
-
const candidate =
|
|
14082
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13457
14083
|
if (await exists3(candidate)) return candidate;
|
|
13458
14084
|
}
|
|
13459
14085
|
for (const name of INDEX_CANDIDATES) {
|
|
13460
|
-
const candidate =
|
|
14086
|
+
const candidate = import_node_path57.default.join(serviceDir, name);
|
|
13461
14087
|
if (await exists3(candidate)) return candidate;
|
|
13462
14088
|
}
|
|
13463
14089
|
return null;
|
|
13464
14090
|
}
|
|
13465
14091
|
function dispatchEntry(entryFile, pkg) {
|
|
13466
|
-
const ext =
|
|
14092
|
+
const ext = import_node_path57.default.extname(entryFile).toLowerCase();
|
|
13467
14093
|
if (ext === ".ts" || ext === ".tsx") return "ts";
|
|
13468
14094
|
if (ext === ".mjs") return "esm";
|
|
13469
14095
|
if (ext === ".cjs") return "cjs";
|
|
@@ -13480,9 +14106,9 @@ function otelInitContents(flavor) {
|
|
|
13480
14106
|
return OTEL_INIT_CJS;
|
|
13481
14107
|
}
|
|
13482
14108
|
function injectionLine(flavor, entryFile, otelInitFile) {
|
|
13483
|
-
let rel =
|
|
14109
|
+
let rel = import_node_path57.default.relative(import_node_path57.default.dirname(entryFile), otelInitFile);
|
|
13484
14110
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
13485
|
-
rel = rel.split(
|
|
14111
|
+
rel = rel.split(import_node_path57.default.sep).join("/");
|
|
13486
14112
|
if (flavor === "cjs") return `require('${rel}')`;
|
|
13487
14113
|
if (flavor === "esm") return `import '${rel}'`;
|
|
13488
14114
|
const tsRel = rel.replace(/\.ts$/, "");
|
|
@@ -13495,27 +14121,27 @@ function lineIsOtelInjection(line) {
|
|
|
13495
14121
|
}
|
|
13496
14122
|
async function detectsSrcLayout(serviceDir) {
|
|
13497
14123
|
const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
|
|
13498
|
-
exists3(
|
|
13499
|
-
exists3(
|
|
13500
|
-
exists3(
|
|
13501
|
-
exists3(
|
|
14124
|
+
exists3(import_node_path57.default.join(serviceDir, "src", "app")),
|
|
14125
|
+
exists3(import_node_path57.default.join(serviceDir, "src", "pages")),
|
|
14126
|
+
exists3(import_node_path57.default.join(serviceDir, "app")),
|
|
14127
|
+
exists3(import_node_path57.default.join(serviceDir, "pages"))
|
|
13502
14128
|
]);
|
|
13503
14129
|
return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
|
|
13504
14130
|
}
|
|
13505
14131
|
async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
|
|
13506
14132
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13507
14133
|
const srcLayout = await detectsSrcLayout(serviceDir);
|
|
13508
|
-
const baseDir = srcLayout ?
|
|
13509
|
-
const instrumentationFile =
|
|
13510
|
-
const instrumentationNodeFile =
|
|
14134
|
+
const baseDir = srcLayout ? import_node_path57.default.join(serviceDir, "src") : serviceDir;
|
|
14135
|
+
const instrumentationFile = import_node_path57.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
|
|
14136
|
+
const instrumentationNodeFile = import_node_path57.default.join(
|
|
13511
14137
|
baseDir,
|
|
13512
14138
|
useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
|
|
13513
14139
|
);
|
|
13514
|
-
const instrumentationEdgeFile =
|
|
14140
|
+
const instrumentationEdgeFile = import_node_path57.default.join(
|
|
13515
14141
|
baseDir,
|
|
13516
14142
|
useTs ? "instrumentation.edge.ts" : "instrumentation.edge.js"
|
|
13517
14143
|
);
|
|
13518
|
-
const envNeatFile =
|
|
14144
|
+
const envNeatFile = import_node_path57.default.join(baseDir, ".env.neat");
|
|
13519
14145
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
13520
14146
|
const dependencyEdits = [];
|
|
13521
14147
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -13640,7 +14266,7 @@ function buildDependencyEdits(pkg, manifestPath) {
|
|
|
13640
14266
|
return edits;
|
|
13641
14267
|
}
|
|
13642
14268
|
async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
|
|
13643
|
-
const envNeatFile =
|
|
14269
|
+
const envNeatFile = import_node_path57.default.join(serviceDir, ".env.neat");
|
|
13644
14270
|
if (!await exists3(envNeatFile)) {
|
|
13645
14271
|
generatedFiles.push({
|
|
13646
14272
|
file: envNeatFile,
|
|
@@ -13675,7 +14301,7 @@ function fileImportsOtelHook(raw, specifiers) {
|
|
|
13675
14301
|
}
|
|
13676
14302
|
async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
13677
14303
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13678
|
-
const otelServerFile =
|
|
14304
|
+
const otelServerFile = import_node_path57.default.join(
|
|
13679
14305
|
serviceDir,
|
|
13680
14306
|
useTs ? "app/otel.server.ts" : "app/otel.server.js"
|
|
13681
14307
|
);
|
|
@@ -13732,11 +14358,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
|
13732
14358
|
}
|
|
13733
14359
|
async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
|
|
13734
14360
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13735
|
-
const otelInitFile =
|
|
14361
|
+
const otelInitFile = import_node_path57.default.join(
|
|
13736
14362
|
serviceDir,
|
|
13737
14363
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
13738
14364
|
);
|
|
13739
|
-
const resolvedHooksFile = hooksFile ??
|
|
14365
|
+
const resolvedHooksFile = hooksFile ?? import_node_path57.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
|
|
13740
14366
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
13741
14367
|
const generatedFiles = [];
|
|
13742
14368
|
const entrypointEdits = [];
|
|
@@ -13798,11 +14424,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
|
|
|
13798
14424
|
}
|
|
13799
14425
|
async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
13800
14426
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13801
|
-
const otelPluginFile =
|
|
14427
|
+
const otelPluginFile = import_node_path57.default.join(
|
|
13802
14428
|
serviceDir,
|
|
13803
14429
|
useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
|
|
13804
14430
|
);
|
|
13805
|
-
const otelInitFile =
|
|
14431
|
+
const otelInitFile = import_node_path57.default.join(
|
|
13806
14432
|
serviceDir,
|
|
13807
14433
|
useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
|
|
13808
14434
|
);
|
|
@@ -13853,19 +14479,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
|
13853
14479
|
var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
|
|
13854
14480
|
async function findAstroMiddleware(serviceDir) {
|
|
13855
14481
|
for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
|
|
13856
|
-
const candidate =
|
|
14482
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13857
14483
|
if (await exists3(candidate)) return candidate;
|
|
13858
14484
|
}
|
|
13859
14485
|
return null;
|
|
13860
14486
|
}
|
|
13861
14487
|
async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
13862
14488
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13863
|
-
const otelInitFile =
|
|
14489
|
+
const otelInitFile = import_node_path57.default.join(
|
|
13864
14490
|
serviceDir,
|
|
13865
14491
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
13866
14492
|
);
|
|
13867
14493
|
const existingMiddleware = await findAstroMiddleware(serviceDir);
|
|
13868
|
-
const middlewareFile = existingMiddleware ??
|
|
14494
|
+
const middlewareFile = existingMiddleware ?? import_node_path57.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
|
|
13869
14495
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
13870
14496
|
const generatedFiles = [];
|
|
13871
14497
|
const entrypointEdits = [];
|
|
@@ -13961,7 +14587,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
|
|
|
13961
14587
|
}
|
|
13962
14588
|
async function plan(serviceDir, opts) {
|
|
13963
14589
|
const pkg = await readPackageJson2(serviceDir);
|
|
13964
|
-
const manifestPath =
|
|
14590
|
+
const manifestPath = import_node_path57.default.join(serviceDir, "package.json");
|
|
13965
14591
|
const project = opts?.project;
|
|
13966
14592
|
const empty = {
|
|
13967
14593
|
language: "javascript",
|
|
@@ -13996,8 +14622,8 @@ async function plan(serviceDir, opts) {
|
|
|
13996
14622
|
return { ...empty, libOnly: true };
|
|
13997
14623
|
}
|
|
13998
14624
|
const flavor = dispatchEntry(entryFile, pkg);
|
|
13999
|
-
const otelInitFile =
|
|
14000
|
-
const envNeatFile =
|
|
14625
|
+
const otelInitFile = import_node_path57.default.join(import_node_path57.default.dirname(entryFile), otelInitFilename(flavor));
|
|
14626
|
+
const envNeatFile = import_node_path57.default.join(serviceDir, ".env.neat");
|
|
14001
14627
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
14002
14628
|
const dependencyEdits = [];
|
|
14003
14629
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -14066,13 +14692,13 @@ async function plan(serviceDir, opts) {
|
|
|
14066
14692
|
};
|
|
14067
14693
|
}
|
|
14068
14694
|
function isAllowedWritePath(serviceDir, target) {
|
|
14069
|
-
const rel =
|
|
14695
|
+
const rel = import_node_path57.default.relative(serviceDir, target);
|
|
14070
14696
|
if (rel.startsWith("..")) return false;
|
|
14071
|
-
const base =
|
|
14697
|
+
const base = import_node_path57.default.basename(target);
|
|
14072
14698
|
if (base === "package.json") return true;
|
|
14073
14699
|
if (base === ".env.neat") return true;
|
|
14074
14700
|
if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
14075
|
-
const relPosix = rel.split(
|
|
14701
|
+
const relPosix = rel.split(import_node_path57.default.sep).join("/");
|
|
14076
14702
|
if (/^instrumentation(?:\.(?:node|edge))?\.(?:js|cjs|mjs|ts)$/.test(base)) {
|
|
14077
14703
|
if (relPosix === base) return true;
|
|
14078
14704
|
if (relPosix === `src/${base}`) return true;
|
|
@@ -14089,7 +14715,7 @@ function isAllowedWritePath(serviceDir, target) {
|
|
|
14089
14715
|
return false;
|
|
14090
14716
|
}
|
|
14091
14717
|
async function writeAtomic(file, contents) {
|
|
14092
|
-
await import_node_fs37.promises.mkdir(
|
|
14718
|
+
await import_node_fs37.promises.mkdir(import_node_path57.default.dirname(file), { recursive: true });
|
|
14093
14719
|
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
14094
14720
|
await import_node_fs37.promises.writeFile(tmp, contents, "utf8");
|
|
14095
14721
|
await import_node_fs37.promises.rename(tmp, file);
|
|
@@ -14273,7 +14899,7 @@ async function rollback(installPlan, originals, createdFiles) {
|
|
|
14273
14899
|
...removed.map((f) => `removed: ${f}`),
|
|
14274
14900
|
""
|
|
14275
14901
|
];
|
|
14276
|
-
const rollbackPath =
|
|
14902
|
+
const rollbackPath = import_node_path57.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
14277
14903
|
await import_node_fs37.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
14278
14904
|
}
|
|
14279
14905
|
function injectInstrumentationHook(raw) {
|
|
@@ -14304,7 +14930,7 @@ var javascriptInstaller = {
|
|
|
14304
14930
|
// src/installers/python.ts
|
|
14305
14931
|
init_cjs_shims();
|
|
14306
14932
|
var import_node_fs38 = require("fs");
|
|
14307
|
-
var
|
|
14933
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
14308
14934
|
var SDK_PACKAGES2 = [
|
|
14309
14935
|
{ name: "opentelemetry-distro", version: ">=0.49b0" },
|
|
14310
14936
|
{ name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
|
|
@@ -14325,7 +14951,7 @@ async function exists4(p) {
|
|
|
14325
14951
|
async function detect2(serviceDir) {
|
|
14326
14952
|
const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
|
|
14327
14953
|
for (const m of markers) {
|
|
14328
|
-
if (await exists4(
|
|
14954
|
+
if (await exists4(import_node_path58.default.join(serviceDir, m))) return true;
|
|
14329
14955
|
}
|
|
14330
14956
|
return false;
|
|
14331
14957
|
}
|
|
@@ -14335,7 +14961,7 @@ function reqPackageName(line) {
|
|
|
14335
14961
|
return head.replace(/[<>=!~].*$/, "").toLowerCase();
|
|
14336
14962
|
}
|
|
14337
14963
|
async function planRequirementsTxtEdits(serviceDir) {
|
|
14338
|
-
const file =
|
|
14964
|
+
const file = import_node_path58.default.join(serviceDir, "requirements.txt");
|
|
14339
14965
|
if (!await exists4(file)) return null;
|
|
14340
14966
|
const raw = await import_node_fs38.promises.readFile(file, "utf8");
|
|
14341
14967
|
const presentNames = new Set(
|
|
@@ -14345,7 +14971,7 @@ async function planRequirementsTxtEdits(serviceDir) {
|
|
|
14345
14971
|
return { manifest: file, missing: [...missing] };
|
|
14346
14972
|
}
|
|
14347
14973
|
async function planProcfileEdits(serviceDir) {
|
|
14348
|
-
const procfile =
|
|
14974
|
+
const procfile = import_node_path58.default.join(serviceDir, "Procfile");
|
|
14349
14975
|
if (!await exists4(procfile)) return [];
|
|
14350
14976
|
const raw = await import_node_fs38.promises.readFile(procfile, "utf8");
|
|
14351
14977
|
const edits = [];
|
|
@@ -14434,7 +15060,7 @@ async function apply2(installPlan) {
|
|
|
14434
15060
|
if (raw === void 0) {
|
|
14435
15061
|
throw new Error(`python installer: cannot read ${file} during apply`);
|
|
14436
15062
|
}
|
|
14437
|
-
const base =
|
|
15063
|
+
const base = import_node_path58.default.basename(file);
|
|
14438
15064
|
if (base === "requirements.txt") {
|
|
14439
15065
|
const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
|
|
14440
15066
|
if (edits.length > 0) {
|
|
@@ -14473,7 +15099,7 @@ async function rollback2(installPlan, originals) {
|
|
|
14473
15099
|
...restored.map((f) => `restored: ${f}`),
|
|
14474
15100
|
""
|
|
14475
15101
|
];
|
|
14476
|
-
const rollbackPath =
|
|
15102
|
+
const rollbackPath = import_node_path58.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
14477
15103
|
await import_node_fs38.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
14478
15104
|
}
|
|
14479
15105
|
var pythonInstaller = {
|
|
@@ -14600,7 +15226,7 @@ init_cjs_shims();
|
|
|
14600
15226
|
var import_node_fs39 = require("fs");
|
|
14601
15227
|
var import_node_http = __toESM(require("http"), 1);
|
|
14602
15228
|
var import_node_net = __toESM(require("net"), 1);
|
|
14603
|
-
var
|
|
15229
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
14604
15230
|
var import_node_url4 = require("url");
|
|
14605
15231
|
var import_node_child_process3 = require("child_process");
|
|
14606
15232
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
@@ -14610,7 +15236,7 @@ async function extractAndPersist(opts) {
|
|
|
14610
15236
|
const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
|
|
14611
15237
|
resetGraph(graphKey);
|
|
14612
15238
|
const graph = getGraph(graphKey);
|
|
14613
|
-
const projectPaths = pathsForProject(graphKey,
|
|
15239
|
+
const projectPaths = pathsForProject(graphKey, import_node_path59.default.join(opts.scanPath, "neat-out"));
|
|
14614
15240
|
const extraction = await extractFromDirectory(graph, opts.scanPath, {
|
|
14615
15241
|
errorsPath: projectPaths.errorsPath
|
|
14616
15242
|
});
|
|
@@ -14662,7 +15288,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14662
15288
|
libOnly++;
|
|
14663
15289
|
const appDeps = svc.pkg ? appFrameworkDependencies(svc.pkg) : [];
|
|
14664
15290
|
if (appDeps.length > 0) {
|
|
14665
|
-
const svcName =
|
|
15291
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14666
15292
|
const list = appDeps.join(", ");
|
|
14667
15293
|
console.warn(
|
|
14668
15294
|
`neat: runtime layer won't engage for ${svcName}: no entry point found.
|
|
@@ -14675,7 +15301,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14675
15301
|
console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
|
|
14676
15302
|
} else if (outcome.outcome === "react-native") {
|
|
14677
15303
|
reactNative++;
|
|
14678
|
-
const svcName =
|
|
15304
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14679
15305
|
console.log(
|
|
14680
15306
|
`neat: ${svc.dir} detected as React Native / Expo
|
|
14681
15307
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14686,7 +15312,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14686
15312
|
);
|
|
14687
15313
|
} else if (outcome.outcome === "bun") {
|
|
14688
15314
|
bun++;
|
|
14689
|
-
const svcName =
|
|
15315
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14690
15316
|
console.log(
|
|
14691
15317
|
`neat: ${svc.dir} detected as Bun
|
|
14692
15318
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14697,7 +15323,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14697
15323
|
);
|
|
14698
15324
|
} else if (outcome.outcome === "deno") {
|
|
14699
15325
|
deno++;
|
|
14700
|
-
const svcName =
|
|
15326
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14701
15327
|
console.log(
|
|
14702
15328
|
`neat: ${svc.dir} detected as Deno
|
|
14703
15329
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14708,7 +15334,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14708
15334
|
);
|
|
14709
15335
|
} else if (outcome.outcome === "cloudflare-workers") {
|
|
14710
15336
|
cloudflareWorkers++;
|
|
14711
|
-
const svcName =
|
|
15337
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14712
15338
|
console.log(
|
|
14713
15339
|
`neat: ${svc.dir} detected as Cloudflare Workers
|
|
14714
15340
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14719,7 +15345,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14719
15345
|
);
|
|
14720
15346
|
} else if (outcome.outcome === "electron") {
|
|
14721
15347
|
electron++;
|
|
14722
|
-
const svcName =
|
|
15348
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14723
15349
|
console.log(
|
|
14724
15350
|
`neat: ${svc.dir} detected as Electron
|
|
14725
15351
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14732,7 +15358,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14732
15358
|
if (svc.pkg && (outcome.outcome === "instrumented" || outcome.outcome === "already-instrumented")) {
|
|
14733
15359
|
const gaps = uninstrumentedLibraries(svc.pkg);
|
|
14734
15360
|
if (gaps.length > 0) {
|
|
14735
|
-
const svcName =
|
|
15361
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14736
15362
|
const list = gaps.join(", ");
|
|
14737
15363
|
const subject = gaps.length === 1 ? "this library" : "these libraries";
|
|
14738
15364
|
const aux = gaps.length === 1 ? "isn't" : "aren't";
|
|
@@ -14938,8 +15564,8 @@ async function persistedPortsFor(scanPath) {
|
|
|
14938
15564
|
return { rest: record.ports.rest, otlp: record.ports.otlp, web: record.ports.web };
|
|
14939
15565
|
}
|
|
14940
15566
|
async function acquireSpawnLock(scanPath) {
|
|
14941
|
-
const lockPath =
|
|
14942
|
-
await import_node_fs39.promises.mkdir(
|
|
15567
|
+
const lockPath = import_node_path59.default.join(scanPath, "neat-out", "daemon.spawn.lock");
|
|
15568
|
+
await import_node_fs39.promises.mkdir(import_node_path59.default.dirname(lockPath), { recursive: true });
|
|
14943
15569
|
const STALE_LOCK_MS = 6e4;
|
|
14944
15570
|
try {
|
|
14945
15571
|
const fd = await import_node_fs39.promises.open(lockPath, "wx");
|
|
@@ -14984,13 +15610,13 @@ async function healthIsForProject(restPort, project) {
|
|
|
14984
15610
|
return false;
|
|
14985
15611
|
}
|
|
14986
15612
|
function daemonLogPath(projectPath2) {
|
|
14987
|
-
return
|
|
15613
|
+
return import_node_path59.default.join(projectPath2, "neat-out", "daemon.log");
|
|
14988
15614
|
}
|
|
14989
15615
|
function spawnDaemonDetached(spec) {
|
|
14990
|
-
const here =
|
|
15616
|
+
const here = import_node_path59.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
|
|
14991
15617
|
const candidates = [
|
|
14992
|
-
|
|
14993
|
-
|
|
15618
|
+
import_node_path59.default.join(here, "neatd.cjs"),
|
|
15619
|
+
import_node_path59.default.join(here, "neatd.js")
|
|
14994
15620
|
];
|
|
14995
15621
|
let entry2 = null;
|
|
14996
15622
|
const fsSync = require("fs");
|
|
@@ -15020,7 +15646,7 @@ function spawnDaemonDetached(spec) {
|
|
|
15020
15646
|
let logFd = null;
|
|
15021
15647
|
if (spec) {
|
|
15022
15648
|
const logPath = daemonLogPath(spec.projectPath);
|
|
15023
|
-
fsSync.mkdirSync(
|
|
15649
|
+
fsSync.mkdirSync(import_node_path59.default.dirname(logPath), { recursive: true });
|
|
15024
15650
|
logFd = fsSync.openSync(logPath, "a");
|
|
15025
15651
|
}
|
|
15026
15652
|
const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
|
|
@@ -15219,7 +15845,7 @@ async function runOrchestrator(opts) {
|
|
|
15219
15845
|
result.steps.browser = openBrowser(dashboardUrl);
|
|
15220
15846
|
}
|
|
15221
15847
|
const daemonRunning = result.steps.daemon === "spawned" || result.steps.daemon === "already-running";
|
|
15222
|
-
const daemonLog = daemonRunning ?
|
|
15848
|
+
const daemonLog = daemonRunning ? import_node_path59.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
|
|
15223
15849
|
printSummary(result, graph, dashboardUrl, daemonLog);
|
|
15224
15850
|
return result;
|
|
15225
15851
|
}
|
|
@@ -15299,6 +15925,8 @@ function parseConnectorArgs(args) {
|
|
|
15299
15925
|
positional: [],
|
|
15300
15926
|
skipValidate: false,
|
|
15301
15927
|
plaintext: false,
|
|
15928
|
+
help: false,
|
|
15929
|
+
json: false,
|
|
15302
15930
|
fields: {}
|
|
15303
15931
|
};
|
|
15304
15932
|
const positional = [];
|
|
@@ -15312,6 +15940,14 @@ function parseConnectorArgs(args) {
|
|
|
15312
15940
|
out.plaintext = true;
|
|
15313
15941
|
continue;
|
|
15314
15942
|
}
|
|
15943
|
+
if (arg === "--help" || arg === "-h") {
|
|
15944
|
+
out.help = true;
|
|
15945
|
+
continue;
|
|
15946
|
+
}
|
|
15947
|
+
if (arg === "--json") {
|
|
15948
|
+
out.json = true;
|
|
15949
|
+
continue;
|
|
15950
|
+
}
|
|
15315
15951
|
if (arg.startsWith("--")) {
|
|
15316
15952
|
let flag = arg;
|
|
15317
15953
|
let value;
|
|
@@ -15388,19 +16024,20 @@ function plaintextFields(ref) {
|
|
|
15388
16024
|
async function connectorAdd(args, deps) {
|
|
15389
16025
|
let provider = args.positional[0];
|
|
15390
16026
|
if (!provider && deps.interactive) {
|
|
15391
|
-
provider = (await deps.prompt(`Provider (${
|
|
16027
|
+
provider = (await deps.prompt(`Provider (${knownProviderNames().join(", ")}):`)).trim();
|
|
15392
16028
|
}
|
|
15393
16029
|
if (!provider) {
|
|
15394
16030
|
deps.err("neat connector add: a provider is required (e.g. `neat connector add supabase`)");
|
|
15395
16031
|
return 2;
|
|
15396
16032
|
}
|
|
15397
|
-
const dispatch =
|
|
16033
|
+
const dispatch = getProviderFieldSchema(provider);
|
|
15398
16034
|
if (!dispatch) {
|
|
15399
16035
|
deps.err(
|
|
15400
|
-
`neat connector add: unknown provider "${provider}". known providers: ${
|
|
16036
|
+
`neat connector add: unknown provider "${provider}". known providers: ${knownProviderNames().join(", ")}`
|
|
15401
16037
|
);
|
|
15402
16038
|
return 2;
|
|
15403
16039
|
}
|
|
16040
|
+
const push = isPushProvider(provider);
|
|
15404
16041
|
let project = args.project;
|
|
15405
16042
|
if (project === void 0 && deps.interactive) {
|
|
15406
16043
|
const answer = (await deps.prompt("Project (blank = the project the daemon is bootstrapping):")).trim();
|
|
@@ -15439,7 +16076,7 @@ async function connectorAdd(args, deps) {
|
|
|
15439
16076
|
credential,
|
|
15440
16077
|
...Object.keys(options).length > 0 ? { options } : {}
|
|
15441
16078
|
};
|
|
15442
|
-
if (!args.skipValidate) {
|
|
16079
|
+
if (!push && !args.skipValidate) {
|
|
15443
16080
|
const outcome = await validateConnectorEntry(entry2, deps.env, deps.fetchImpl);
|
|
15444
16081
|
const code = reportPreWriteValidation(outcome, deps);
|
|
15445
16082
|
if (code !== 0) return code;
|
|
@@ -15450,14 +16087,32 @@ async function connectorAdd(args, deps) {
|
|
|
15450
16087
|
`neat connector add: storing a literal secret at rest for ${plaintext.join(", ")} \u2014 prefer an env-var reference like \`$PROVIDER_TOKEN\` (pass --plaintext to silence this).`
|
|
15451
16088
|
);
|
|
15452
16089
|
}
|
|
16090
|
+
if (push) {
|
|
16091
|
+
if (args.skipValidate) {
|
|
16092
|
+
deps.out("note: --skip-validate has no effect for a push provider \u2014 provisioning the drain is the add.");
|
|
16093
|
+
}
|
|
16094
|
+
const outcome = await provisionConnector(entry2, deps.env, deps.fetchImpl);
|
|
16095
|
+
if (outcome.status !== "ok") {
|
|
16096
|
+
deps.err(
|
|
16097
|
+
`neat connector add: could not provision the ${provider} connector \u2014 ${outcome.reason}. Nothing was written.`
|
|
16098
|
+
);
|
|
16099
|
+
return 1;
|
|
16100
|
+
}
|
|
16101
|
+
if (outcome.options) entry2.options = { ...entry2.options ?? {}, ...outcome.options };
|
|
16102
|
+
if (outcome.note) deps.out(`note: ${outcome.note}`);
|
|
16103
|
+
}
|
|
15453
16104
|
const { replaced } = await upsertConnectorEntry(entry2, deps.home);
|
|
15454
16105
|
const verb = replaced ? "updated" : "added";
|
|
15455
16106
|
const where = project ? `project "${project}"` : "the bootstrapping project";
|
|
15456
16107
|
deps.out(`${verb} connector "${id}" (${provider}) for ${where}.`);
|
|
15457
|
-
if (
|
|
15458
|
-
deps.out("
|
|
16108
|
+
if (push) {
|
|
16109
|
+
deps.out("The drain is live \u2014 traces arrive at the daemon's OTLP receiver. No poll or restart needed.");
|
|
16110
|
+
} else {
|
|
16111
|
+
if (args.skipValidate) {
|
|
16112
|
+
deps.out("skipped validation (--skip-validate) \u2014 the credential is checked at the next daemon poll.");
|
|
16113
|
+
}
|
|
16114
|
+
deps.out("Restart the project's daemon (or start it) to begin polling this connector.");
|
|
15459
16115
|
}
|
|
15460
|
-
deps.out("Restart the project's daemon (or start it) to begin polling this connector.");
|
|
15461
16116
|
return 0;
|
|
15462
16117
|
}
|
|
15463
16118
|
function reportPreWriteValidation(outcome, deps) {
|
|
@@ -15483,10 +16138,26 @@ function reportPreWriteValidation(outcome, deps) {
|
|
|
15483
16138
|
return 2;
|
|
15484
16139
|
}
|
|
15485
16140
|
}
|
|
15486
|
-
async function connectorList(deps, filterProject) {
|
|
16141
|
+
async function connectorList(deps, filterProject, json = false) {
|
|
15487
16142
|
const config = await readConnectorsConfig(deps.home);
|
|
15488
16143
|
let entries = config.connectors;
|
|
15489
16144
|
if (filterProject) entries = entries.filter((e) => e.project === filterProject);
|
|
16145
|
+
if (json) {
|
|
16146
|
+
const rows = entries.map((e) => ({
|
|
16147
|
+
id: e.id,
|
|
16148
|
+
provider: e.provider,
|
|
16149
|
+
project: e.project ?? null,
|
|
16150
|
+
kind: isPushProvider(e.provider) ? "push" : "pull",
|
|
16151
|
+
credential: describeCredential(e.credential, deps.env).map((c) => ({
|
|
16152
|
+
...c.field ? { field: c.field } : {},
|
|
16153
|
+
ref: c.display,
|
|
16154
|
+
...c.status ? { status: c.status } : { kind: c.kind }
|
|
16155
|
+
})),
|
|
16156
|
+
...e.options ? { options: e.options } : {}
|
|
16157
|
+
}));
|
|
16158
|
+
deps.out(JSON.stringify(rows, null, 2));
|
|
16159
|
+
return 0;
|
|
16160
|
+
}
|
|
15490
16161
|
if (entries.length === 0) {
|
|
15491
16162
|
deps.out(
|
|
15492
16163
|
filterProject ? `no connectors configured for project "${filterProject}".` : "no connectors configured. run `neat connector add <provider>` to add one."
|
|
@@ -15509,6 +16180,22 @@ async function connectorRemove(deps, id) {
|
|
|
15509
16180
|
deps.err("neat connector remove: missing <id>. run `neat connector list` to see configured ids.");
|
|
15510
16181
|
return 2;
|
|
15511
16182
|
}
|
|
16183
|
+
const config = await readConnectorsConfig(deps.home);
|
|
16184
|
+
const entry2 = config.connectors.find((c) => c.id === id);
|
|
16185
|
+
if (!entry2) {
|
|
16186
|
+
deps.err(`neat connector remove: no connector with id "${id}". run \`neat connector list\` to see configured ids.`);
|
|
16187
|
+
return 1;
|
|
16188
|
+
}
|
|
16189
|
+
if (isPushProvider(entry2.provider)) {
|
|
16190
|
+
const outcome = await deprovisionConnector(entry2, deps.env, deps.fetchImpl);
|
|
16191
|
+
if (outcome.status !== "ok") {
|
|
16192
|
+
deps.err(
|
|
16193
|
+
`neat connector remove: could not delete the ${entry2.provider} drain \u2014 ${outcome.reason}. The entry was kept so you can retry (or delete the drain in the Vercel dashboard, then re-run).`
|
|
16194
|
+
);
|
|
16195
|
+
return 1;
|
|
16196
|
+
}
|
|
16197
|
+
if (outcome.note) deps.out(`note: ${outcome.note}`);
|
|
16198
|
+
}
|
|
15512
16199
|
const removed = await removeConnectorEntry(id, deps.home);
|
|
15513
16200
|
if (!removed) {
|
|
15514
16201
|
deps.err(`neat connector remove: no connector with id "${id}". run \`neat connector list\` to see configured ids.`);
|
|
@@ -15549,13 +16236,20 @@ async function connectorTest(deps, id) {
|
|
|
15549
16236
|
}
|
|
15550
16237
|
function printConnectorUsage(write) {
|
|
15551
16238
|
write("usage: neat connector <add|list|remove|test> [args]");
|
|
16239
|
+
write(` providers: ${knownProviderNames().join(", ")}`);
|
|
16240
|
+
write(" pull (polled): supabase, railway, firebase, cloudflare");
|
|
16241
|
+
write(" push (drains): vercel \u2014 provisions a Vercel trace Drain that forwards");
|
|
16242
|
+
write(" traces to the daemon's OTLP receiver; no app instrumentation");
|
|
15552
16243
|
write(" add <provider> add a connector; validates the credential first (--skip-validate to skip)");
|
|
15553
16244
|
write(" flags: --project <name> --credential/--token <$VAR|value> --id <id>");
|
|
15554
16245
|
write(" --<option> <value> (provider-specific) --plaintext --skip-validate");
|
|
16246
|
+
write(" vercel: neat connector add vercel --token $VERCEL_TOKEN \\");
|
|
16247
|
+
write(" --otel-token $NEAT_OTEL_TOKEN --team-id <teamId> \\");
|
|
16248
|
+
write(" --endpoint https://<public-host>/v1/traces [--project-ids <id,id>]");
|
|
15555
16249
|
write(" list list configured connectors (credentials shown redacted)");
|
|
15556
16250
|
write(" flags: --project <name>");
|
|
15557
|
-
write(" remove <id> remove a connector by id");
|
|
15558
|
-
write(" test <id> re-run
|
|
16251
|
+
write(" remove <id> remove a connector by id (a push provider also has its drain deleted)");
|
|
16252
|
+
write(" test <id> re-run validation for an existing connector (a push provider re-tests its drain)");
|
|
15559
16253
|
}
|
|
15560
16254
|
async function runConnectorCommand(rawArgs, deps = {}) {
|
|
15561
16255
|
const resolved = resolveDeps(deps);
|
|
@@ -15565,11 +16259,15 @@ async function runConnectorCommand(rawArgs, deps = {}) {
|
|
|
15565
16259
|
return 2;
|
|
15566
16260
|
}
|
|
15567
16261
|
const a = parsed.value;
|
|
16262
|
+
if (a.help) {
|
|
16263
|
+
printConnectorUsage(resolved.out);
|
|
16264
|
+
return 0;
|
|
16265
|
+
}
|
|
15568
16266
|
switch (a.subcommand) {
|
|
15569
16267
|
case "add":
|
|
15570
16268
|
return connectorAdd(a, resolved);
|
|
15571
16269
|
case "list":
|
|
15572
|
-
return connectorList(resolved, a.project);
|
|
16270
|
+
return connectorList(resolved, a.project, a.json);
|
|
15573
16271
|
case "remove":
|
|
15574
16272
|
return connectorRemove(resolved, a.positional[0]);
|
|
15575
16273
|
case "test":
|
|
@@ -15585,13 +16283,187 @@ async function runConnectorCommand(rawArgs, deps = {}) {
|
|
|
15585
16283
|
}
|
|
15586
16284
|
}
|
|
15587
16285
|
|
|
16286
|
+
// src/hooks-cli.ts
|
|
16287
|
+
init_cjs_shims();
|
|
16288
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
16289
|
+
var import_node_os5 = __toESM(require("os"), 1);
|
|
16290
|
+
var import_node_fs40 = require("fs");
|
|
16291
|
+
var import_node_url5 = require("url");
|
|
16292
|
+
var HOOK_FILENAME = "neat-search-nudge.mjs";
|
|
16293
|
+
var GUIDE_FILENAME = "GRAPH_FIRST.md";
|
|
16294
|
+
var GUIDE_INSTALL_NAME = "neat-graph-first.md";
|
|
16295
|
+
var HOOK_MATCHER = "Grep|Glob|Bash";
|
|
16296
|
+
function moduleDir() {
|
|
16297
|
+
return typeof __dirname !== "undefined" ? __dirname : import_node_path60.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
|
|
16298
|
+
}
|
|
16299
|
+
async function readSkillAsset(rel) {
|
|
16300
|
+
const here = moduleDir();
|
|
16301
|
+
const candidates = [
|
|
16302
|
+
import_node_path60.default.resolve(here, "../../claude-skill", rel),
|
|
16303
|
+
import_node_path60.default.resolve(here, "../../../claude-skill", rel),
|
|
16304
|
+
import_node_path60.default.resolve(here, "../claude-skill", rel)
|
|
16305
|
+
];
|
|
16306
|
+
for (const candidate of candidates) {
|
|
16307
|
+
try {
|
|
16308
|
+
return await import_node_fs40.promises.readFile(candidate, "utf8");
|
|
16309
|
+
} catch {
|
|
16310
|
+
}
|
|
16311
|
+
}
|
|
16312
|
+
throw new Error(
|
|
16313
|
+
`neat hooks: could not find @neat.is/claude-skill/${rel} \u2014 is the package installed?`
|
|
16314
|
+
);
|
|
16315
|
+
}
|
|
16316
|
+
function neatHome3() {
|
|
16317
|
+
const override = process.env.NEAT_HOME;
|
|
16318
|
+
if (override && override.length > 0) return import_node_path60.default.resolve(override);
|
|
16319
|
+
return import_node_path60.default.join(import_node_os5.default.homedir(), ".neat");
|
|
16320
|
+
}
|
|
16321
|
+
function claudeSettingsPath() {
|
|
16322
|
+
const override = process.env.NEAT_CLAUDE_SETTINGS;
|
|
16323
|
+
if (override && override.length > 0) return import_node_path60.default.resolve(override);
|
|
16324
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os5.default.homedir();
|
|
16325
|
+
return import_node_path60.default.join(home, ".claude", "settings.json");
|
|
16326
|
+
}
|
|
16327
|
+
function installedHookPath() {
|
|
16328
|
+
return import_node_path60.default.join(neatHome3(), "hooks", HOOK_FILENAME);
|
|
16329
|
+
}
|
|
16330
|
+
function isNeatSearchEntry(entry2) {
|
|
16331
|
+
return (entry2.hooks ?? []).some(
|
|
16332
|
+
(h) => typeof h.command === "string" && h.command.includes(HOOK_FILENAME)
|
|
16333
|
+
);
|
|
16334
|
+
}
|
|
16335
|
+
function neatHookEntry(command) {
|
|
16336
|
+
return { matcher: HOOK_MATCHER, hooks: [{ type: "command", command }] };
|
|
16337
|
+
}
|
|
16338
|
+
function hookCommand(scriptPath) {
|
|
16339
|
+
return `node "${scriptPath}"`;
|
|
16340
|
+
}
|
|
16341
|
+
async function runHooks(opts) {
|
|
16342
|
+
if (opts.printHook) {
|
|
16343
|
+
process.stdout.write(await readSkillAsset(`hooks/${HOOK_FILENAME}`));
|
|
16344
|
+
return { exitCode: 0 };
|
|
16345
|
+
}
|
|
16346
|
+
if (opts.printGuide) {
|
|
16347
|
+
process.stdout.write(await readSkillAsset(GUIDE_FILENAME));
|
|
16348
|
+
return { exitCode: 0 };
|
|
16349
|
+
}
|
|
16350
|
+
if (opts.printSettings) {
|
|
16351
|
+
const block = {
|
|
16352
|
+
hooks: { PreToolUse: [neatHookEntry(hookCommand(installedHookPath()))] }
|
|
16353
|
+
};
|
|
16354
|
+
process.stdout.write(JSON.stringify(block, null, 2) + "\n");
|
|
16355
|
+
return { exitCode: 0 };
|
|
16356
|
+
}
|
|
16357
|
+
if (opts.apply) {
|
|
16358
|
+
const hookScript = await readSkillAsset(`hooks/${HOOK_FILENAME}`);
|
|
16359
|
+
const guide = await readSkillAsset(GUIDE_FILENAME);
|
|
16360
|
+
const scriptPath = installedHookPath();
|
|
16361
|
+
await import_node_fs40.promises.mkdir(import_node_path60.default.dirname(scriptPath), { recursive: true });
|
|
16362
|
+
await import_node_fs40.promises.writeFile(scriptPath, hookScript, { mode: 493 });
|
|
16363
|
+
const guidePath = import_node_path60.default.join(neatHome3(), GUIDE_INSTALL_NAME);
|
|
16364
|
+
await import_node_fs40.promises.writeFile(guidePath, guide, "utf8");
|
|
16365
|
+
const settingsFile = claudeSettingsPath();
|
|
16366
|
+
let settings = {};
|
|
16367
|
+
try {
|
|
16368
|
+
settings = JSON.parse(await import_node_fs40.promises.readFile(settingsFile, "utf8"));
|
|
16369
|
+
} catch (err) {
|
|
16370
|
+
if (err.code !== "ENOENT") {
|
|
16371
|
+
console.error(
|
|
16372
|
+
`neat hooks: failed to read ${settingsFile} \u2014 ${err.message}`
|
|
16373
|
+
);
|
|
16374
|
+
return { exitCode: 1 };
|
|
16375
|
+
}
|
|
16376
|
+
}
|
|
16377
|
+
const hooks = settings.hooks ?? {};
|
|
16378
|
+
const preToolUse = Array.isArray(hooks.PreToolUse) ? [...hooks.PreToolUse] : [];
|
|
16379
|
+
const command = hookCommand(scriptPath);
|
|
16380
|
+
const existingIdx = preToolUse.findIndex(isNeatSearchEntry);
|
|
16381
|
+
if (existingIdx >= 0) {
|
|
16382
|
+
preToolUse[existingIdx] = neatHookEntry(command);
|
|
16383
|
+
} else {
|
|
16384
|
+
preToolUse.push(neatHookEntry(command));
|
|
16385
|
+
}
|
|
16386
|
+
const merged = {
|
|
16387
|
+
...settings,
|
|
16388
|
+
hooks: { ...hooks, PreToolUse: preToolUse }
|
|
16389
|
+
};
|
|
16390
|
+
await import_node_fs40.promises.mkdir(import_node_path60.default.dirname(settingsFile), { recursive: true });
|
|
16391
|
+
await import_node_fs40.promises.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
16392
|
+
console.log(`neat hooks: installed the search-nudge hook`);
|
|
16393
|
+
console.log(` script: ${scriptPath}`);
|
|
16394
|
+
console.log(` settings: ${settingsFile} (PreToolUse \u2192 ${HOOK_MATCHER})`);
|
|
16395
|
+
console.log(` guidance: ${guidePath}`);
|
|
16396
|
+
console.log("");
|
|
16397
|
+
console.log("restart Claude Code to load the hook. On a Grep/Glob or a Bash grep,");
|
|
16398
|
+
console.log("your agent will now be nudged to query NEAT first.");
|
|
16399
|
+
console.log("");
|
|
16400
|
+
console.log("The hook is Claude-Code-specific. For agents on other harnesses, paste");
|
|
16401
|
+
console.log(`the guidance above into your project instructions (CLAUDE.md / AGENTS.md).`);
|
|
16402
|
+
return { exitCode: 0 };
|
|
16403
|
+
}
|
|
16404
|
+
usage();
|
|
16405
|
+
return { exitCode: 0 };
|
|
16406
|
+
}
|
|
16407
|
+
function usage() {
|
|
16408
|
+
console.log("neat hooks \u2014 wire NEAT into your agent so it queries the graph before grepping");
|
|
16409
|
+
console.log("");
|
|
16410
|
+
console.log(" --apply install the Claude Code search-nudge hook and write the");
|
|
16411
|
+
console.log(" graph-first guidance to ~/.neat/, merging into");
|
|
16412
|
+
console.log(" ~/.claude/settings.json without touching your other hooks");
|
|
16413
|
+
console.log(" --print-hook print the hook script to stdout");
|
|
16414
|
+
console.log(" --print-guide print the agent-agnostic graph-first guidance to stdout");
|
|
16415
|
+
console.log(" --print-settings print the settings.json PreToolUse block --apply would add");
|
|
16416
|
+
console.log("");
|
|
16417
|
+
console.log("The hook is a gentle, non-blocking nudge \u2014 searches still run. It is");
|
|
16418
|
+
console.log("Claude-Code-specific; other harnesses get the same steer from the guidance.");
|
|
16419
|
+
}
|
|
16420
|
+
async function runHooksCommand(args) {
|
|
16421
|
+
const opts = {
|
|
16422
|
+
apply: false,
|
|
16423
|
+
printHook: false,
|
|
16424
|
+
printGuide: false,
|
|
16425
|
+
printSettings: false
|
|
16426
|
+
};
|
|
16427
|
+
for (const arg of args) {
|
|
16428
|
+
switch (arg) {
|
|
16429
|
+
case "--apply":
|
|
16430
|
+
opts.apply = true;
|
|
16431
|
+
break;
|
|
16432
|
+
case "--print-hook":
|
|
16433
|
+
opts.printHook = true;
|
|
16434
|
+
break;
|
|
16435
|
+
case "--print-guide":
|
|
16436
|
+
opts.printGuide = true;
|
|
16437
|
+
break;
|
|
16438
|
+
case "--print-settings":
|
|
16439
|
+
opts.printSettings = true;
|
|
16440
|
+
break;
|
|
16441
|
+
case "-h":
|
|
16442
|
+
case "--help":
|
|
16443
|
+
usage();
|
|
16444
|
+
return 0;
|
|
16445
|
+
default:
|
|
16446
|
+
console.error(`neat hooks: unknown flag "${arg}"`);
|
|
16447
|
+
usage();
|
|
16448
|
+
return 2;
|
|
16449
|
+
}
|
|
16450
|
+
}
|
|
16451
|
+
try {
|
|
16452
|
+
const { exitCode } = await runHooks(opts);
|
|
16453
|
+
return exitCode;
|
|
16454
|
+
} catch (err) {
|
|
16455
|
+
console.error(err.message);
|
|
16456
|
+
return 1;
|
|
16457
|
+
}
|
|
16458
|
+
}
|
|
16459
|
+
|
|
15588
16460
|
// src/cli-verbs.ts
|
|
15589
16461
|
init_cjs_shims();
|
|
15590
|
-
var
|
|
16462
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
15591
16463
|
|
|
15592
16464
|
// src/cli-client.ts
|
|
15593
16465
|
init_cjs_shims();
|
|
15594
|
-
var
|
|
16466
|
+
var import_types49 = require("@neat.is/types");
|
|
15595
16467
|
var HttpError = class extends Error {
|
|
15596
16468
|
constructor(status2, message, responseBody = "") {
|
|
15597
16469
|
super(message);
|
|
@@ -15616,10 +16488,10 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
15616
16488
|
const root = baseUrl.replace(/\/$/, "");
|
|
15617
16489
|
const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
|
|
15618
16490
|
return {
|
|
15619
|
-
async get(
|
|
16491
|
+
async get(path63) {
|
|
15620
16492
|
let res;
|
|
15621
16493
|
try {
|
|
15622
|
-
res = await fetch(`${root}${
|
|
16494
|
+
res = await fetch(`${root}${path63}`, {
|
|
15623
16495
|
headers: { ...authHeader }
|
|
15624
16496
|
});
|
|
15625
16497
|
} catch (err) {
|
|
@@ -15631,16 +16503,16 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
15631
16503
|
const body = await res.text().catch(() => "");
|
|
15632
16504
|
throw new HttpError(
|
|
15633
16505
|
res.status,
|
|
15634
|
-
`${res.status} ${res.statusText} on GET ${
|
|
16506
|
+
`${res.status} ${res.statusText} on GET ${path63}: ${body}`,
|
|
15635
16507
|
body
|
|
15636
16508
|
);
|
|
15637
16509
|
}
|
|
15638
16510
|
return await res.json();
|
|
15639
16511
|
},
|
|
15640
|
-
async post(
|
|
16512
|
+
async post(path63, body) {
|
|
15641
16513
|
let res;
|
|
15642
16514
|
try {
|
|
15643
|
-
res = await fetch(`${root}${
|
|
16515
|
+
res = await fetch(`${root}${path63}`, {
|
|
15644
16516
|
method: "POST",
|
|
15645
16517
|
headers: { "content-type": "application/json", ...authHeader },
|
|
15646
16518
|
body: JSON.stringify(body)
|
|
@@ -15654,7 +16526,7 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
15654
16526
|
const text = await res.text().catch(() => "");
|
|
15655
16527
|
throw new HttpError(
|
|
15656
16528
|
res.status,
|
|
15657
|
-
`${res.status} ${res.statusText} on POST ${
|
|
16529
|
+
`${res.status} ${res.statusText} on POST ${path63}: ${text}`,
|
|
15658
16530
|
text
|
|
15659
16531
|
);
|
|
15660
16532
|
}
|
|
@@ -15668,12 +16540,12 @@ function projectPath(project, suffix) {
|
|
|
15668
16540
|
}
|
|
15669
16541
|
async function runRootCause(client, input) {
|
|
15670
16542
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
15671
|
-
const
|
|
16543
|
+
const path63 = projectPath(
|
|
15672
16544
|
input.project,
|
|
15673
16545
|
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
15674
16546
|
);
|
|
15675
16547
|
try {
|
|
15676
|
-
const result = await client.get(
|
|
16548
|
+
const result = await client.get(path63);
|
|
15677
16549
|
const arrowPath = result.traversalPath.join(" \u2190 ");
|
|
15678
16550
|
const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
|
|
15679
16551
|
const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
|
|
@@ -15699,12 +16571,12 @@ async function runRootCause(client, input) {
|
|
|
15699
16571
|
}
|
|
15700
16572
|
async function runBlastRadius(client, input) {
|
|
15701
16573
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
15702
|
-
const
|
|
16574
|
+
const path63 = projectPath(
|
|
15703
16575
|
input.project,
|
|
15704
16576
|
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
15705
16577
|
);
|
|
15706
16578
|
try {
|
|
15707
|
-
const result = await client.get(
|
|
16579
|
+
const result = await client.get(path63);
|
|
15708
16580
|
if (result.totalAffected === 0) {
|
|
15709
16581
|
return {
|
|
15710
16582
|
summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
|
|
@@ -15733,17 +16605,17 @@ async function runBlastRadius(client, input) {
|
|
|
15733
16605
|
}
|
|
15734
16606
|
}
|
|
15735
16607
|
function formatBlastEntry(n) {
|
|
15736
|
-
const tag = n.edgeProvenance ===
|
|
16608
|
+
const tag = n.edgeProvenance === import_types49.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
|
|
15737
16609
|
return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
|
|
15738
16610
|
}
|
|
15739
16611
|
async function runDependencies(client, input) {
|
|
15740
16612
|
const depth = input.depth ?? 3;
|
|
15741
|
-
const
|
|
16613
|
+
const path63 = projectPath(
|
|
15742
16614
|
input.project,
|
|
15743
16615
|
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
15744
16616
|
);
|
|
15745
16617
|
try {
|
|
15746
|
-
const result = await client.get(
|
|
16618
|
+
const result = await client.get(path63);
|
|
15747
16619
|
if (result.total === 0) {
|
|
15748
16620
|
return {
|
|
15749
16621
|
summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
|
|
@@ -15790,7 +16662,7 @@ async function runObservedDependencies(client, input) {
|
|
|
15790
16662
|
if (result.observed) {
|
|
15791
16663
|
return {
|
|
15792
16664
|
summary: `${input.nodeId} makes no outbound runtime calls, but OTel has observed it receiving traffic on ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 it's a pure receiver.`,
|
|
15793
|
-
provenance:
|
|
16665
|
+
provenance: import_types49.Provenance.OBSERVED
|
|
15794
16666
|
};
|
|
15795
16667
|
}
|
|
15796
16668
|
const note = result.hasExtractedOutbound ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
|
|
@@ -15800,7 +16672,7 @@ async function runObservedDependencies(client, input) {
|
|
|
15800
16672
|
return {
|
|
15801
16673
|
summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? "y" : "ies"} confirmed by OTel.`,
|
|
15802
16674
|
block: blockLines.join("\n"),
|
|
15803
|
-
provenance:
|
|
16675
|
+
provenance: import_types49.Provenance.OBSERVED
|
|
15804
16676
|
};
|
|
15805
16677
|
} catch (err) {
|
|
15806
16678
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -15835,9 +16707,9 @@ function formatDuration(ms) {
|
|
|
15835
16707
|
return `${Math.round(h / 24)}d`;
|
|
15836
16708
|
}
|
|
15837
16709
|
async function runIncidents(client, input) {
|
|
15838
|
-
const
|
|
16710
|
+
const path63 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
15839
16711
|
try {
|
|
15840
|
-
const body = await client.get(
|
|
16712
|
+
const body = await client.get(path63);
|
|
15841
16713
|
const events = body.events;
|
|
15842
16714
|
if (events.length === 0) {
|
|
15843
16715
|
return {
|
|
@@ -15854,7 +16726,7 @@ async function runIncidents(client, input) {
|
|
|
15854
16726
|
return {
|
|
15855
16727
|
summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
|
|
15856
16728
|
block: blockLines.join("\n"),
|
|
15857
|
-
provenance:
|
|
16729
|
+
provenance: import_types49.Provenance.OBSERVED
|
|
15858
16730
|
};
|
|
15859
16731
|
} catch (err) {
|
|
15860
16732
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -15963,7 +16835,7 @@ async function runStaleEdges(client, input) {
|
|
|
15963
16835
|
return {
|
|
15964
16836
|
summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
|
|
15965
16837
|
block: blockLines.join("\n"),
|
|
15966
|
-
provenance:
|
|
16838
|
+
provenance: import_types49.Provenance.STALE
|
|
15967
16839
|
};
|
|
15968
16840
|
}
|
|
15969
16841
|
async function runPolicies(client, input) {
|
|
@@ -16127,7 +16999,7 @@ async function resolveProjectEntry(opts) {
|
|
|
16127
16999
|
const cwd = opts.cwd ?? process.cwd();
|
|
16128
17000
|
const resolvedCwd = await normalizeProjectPath(cwd);
|
|
16129
17001
|
for (const entry2 of entries) {
|
|
16130
|
-
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${
|
|
17002
|
+
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path61.default.sep}`)) {
|
|
16131
17003
|
return entry2;
|
|
16132
17004
|
}
|
|
16133
17005
|
}
|
|
@@ -16280,7 +17152,7 @@ async function runSync(opts) {
|
|
|
16280
17152
|
}
|
|
16281
17153
|
|
|
16282
17154
|
// src/cli.ts
|
|
16283
|
-
var
|
|
17155
|
+
var import_types50 = require("@neat.is/types");
|
|
16284
17156
|
function isNpxInvocation() {
|
|
16285
17157
|
if (process.env.npm_command === "exec") return true;
|
|
16286
17158
|
const execpath = process.env.npm_execpath ?? "";
|
|
@@ -16292,7 +17164,7 @@ function isNpxInvocation() {
|
|
|
16292
17164
|
function commandPrefix() {
|
|
16293
17165
|
return isNpxInvocation() ? "npx neat.is" : "neat";
|
|
16294
17166
|
}
|
|
16295
|
-
function
|
|
17167
|
+
function usage2() {
|
|
16296
17168
|
const neat = commandPrefix();
|
|
16297
17169
|
console.log("Installed via npx? Prefix commands with `npx neat.is`, or install once: `npm i -g neat.is`.");
|
|
16298
17170
|
console.log("");
|
|
@@ -16329,6 +17201,14 @@ function usage() {
|
|
|
16329
17201
|
console.log(" Flags:");
|
|
16330
17202
|
console.log(" --print-config print the JSON snippet to stdout");
|
|
16331
17203
|
console.log(" --apply merge mcpServers.neat into ~/.claude.json");
|
|
17204
|
+
console.log(" hooks Wire NEAT into your agent so it queries the graph before");
|
|
17205
|
+
console.log(" grepping. Installs a gentle Claude Code search-nudge hook and");
|
|
17206
|
+
console.log(" writes agent-agnostic graph-first guidance for other harnesses.");
|
|
17207
|
+
console.log(" Flags:");
|
|
17208
|
+
console.log(" --apply install the hook + guidance");
|
|
17209
|
+
console.log(" --print-hook print the hook script");
|
|
17210
|
+
console.log(" --print-guide print the graph-first guidance");
|
|
17211
|
+
console.log(" --print-settings print the settings.json block --apply adds");
|
|
16332
17212
|
console.log(" deploy Detect the deploy substrate, generate NEAT_AUTH_TOKEN,");
|
|
16333
17213
|
console.log(" emit a docker-compose / systemd / docker run artifact, and");
|
|
16334
17214
|
console.log(" print the OTel env-vars block to paste into your platform.");
|
|
@@ -16341,8 +17221,9 @@ function usage() {
|
|
|
16341
17221
|
console.log(" --dry-run run extraction in-memory; do not write");
|
|
16342
17222
|
console.log(" --no-instrument skip the SDK install apply step");
|
|
16343
17223
|
console.log(" --json emit the delta summary as JSON");
|
|
16344
|
-
console.log(" connector Configure
|
|
16345
|
-
console.log(" firebase, cloudflare).
|
|
17224
|
+
console.log(" connector Configure OBSERVED connectors \u2014 pull (supabase, railway,");
|
|
17225
|
+
console.log(" firebase, cloudflare) and push (vercel, via a trace Drain).");
|
|
17226
|
+
console.log(" Subcommands:");
|
|
16346
17227
|
console.log(" add <provider> add a connector; validates the credential");
|
|
16347
17228
|
console.log(" against the provider first (--skip-validate to skip)");
|
|
16348
17229
|
console.log(" flags: --project <name>, --credential/--token <$VAR|value>,");
|
|
@@ -16573,7 +17454,7 @@ async function buildPatchSections(services, project) {
|
|
|
16573
17454
|
}
|
|
16574
17455
|
async function runInit(opts) {
|
|
16575
17456
|
const written = [];
|
|
16576
|
-
const stat = await
|
|
17457
|
+
const stat = await import_node_fs41.promises.stat(opts.scanPath).catch(() => null);
|
|
16577
17458
|
if (!stat || !stat.isDirectory()) {
|
|
16578
17459
|
console.error(`neat init: ${opts.scanPath} is not a directory`);
|
|
16579
17460
|
return { exitCode: 2, writtenFiles: written };
|
|
@@ -16582,13 +17463,13 @@ async function runInit(opts) {
|
|
|
16582
17463
|
printDiscoveryReport(opts, services);
|
|
16583
17464
|
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
16584
17465
|
const patch = renderPatch(sections);
|
|
16585
|
-
const patchPath =
|
|
17466
|
+
const patchPath = import_node_path62.default.join(opts.scanPath, "neat.patch");
|
|
16586
17467
|
if (opts.dryRun) {
|
|
16587
|
-
await
|
|
17468
|
+
await import_node_fs41.promises.writeFile(patchPath, patch, "utf8");
|
|
16588
17469
|
written.push(patchPath);
|
|
16589
17470
|
console.log(`dry-run: patch written to ${patchPath}`);
|
|
16590
|
-
const gitignorePath =
|
|
16591
|
-
const gitignoreExists = await
|
|
17471
|
+
const gitignorePath = import_node_path62.default.join(opts.scanPath, ".gitignore");
|
|
17472
|
+
const gitignoreExists = await import_node_fs41.promises.stat(gitignorePath).then(() => true).catch(() => false);
|
|
16592
17473
|
const verb = gitignoreExists ? "append" : "create";
|
|
16593
17474
|
console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
|
|
16594
17475
|
console.log("rerun without --dry-run to register and snapshot.");
|
|
@@ -16599,9 +17480,9 @@ async function runInit(opts) {
|
|
|
16599
17480
|
const graph = getGraph(graphKey);
|
|
16600
17481
|
const projectPaths = pathsForProject(
|
|
16601
17482
|
graphKey,
|
|
16602
|
-
|
|
17483
|
+
import_node_path62.default.join(opts.scanPath, "neat-out")
|
|
16603
17484
|
);
|
|
16604
|
-
const errorsPath =
|
|
17485
|
+
const errorsPath = import_node_path62.default.join(import_node_path62.default.dirname(opts.outPath), import_node_path62.default.basename(projectPaths.errorsPath));
|
|
16605
17486
|
const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
|
|
16606
17487
|
await saveGraphToDisk(graph, opts.outPath);
|
|
16607
17488
|
written.push(opts.outPath);
|
|
@@ -16680,7 +17561,7 @@ async function runInit(opts) {
|
|
|
16680
17561
|
console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
|
|
16681
17562
|
}
|
|
16682
17563
|
} else {
|
|
16683
|
-
await
|
|
17564
|
+
await import_node_fs41.promises.writeFile(patchPath, patch, "utf8");
|
|
16684
17565
|
written.push(patchPath);
|
|
16685
17566
|
}
|
|
16686
17567
|
}
|
|
@@ -16720,9 +17601,9 @@ var CLAUDE_SKILL_CONFIG = {
|
|
|
16720
17601
|
};
|
|
16721
17602
|
function claudeConfigPath() {
|
|
16722
17603
|
const override = process.env.NEAT_CLAUDE_CONFIG;
|
|
16723
|
-
if (override && override.length > 0) return
|
|
17604
|
+
if (override && override.length > 0) return import_node_path62.default.resolve(override);
|
|
16724
17605
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
16725
|
-
return
|
|
17606
|
+
return import_node_path62.default.join(home, ".claude.json");
|
|
16726
17607
|
}
|
|
16727
17608
|
async function runSkill(opts) {
|
|
16728
17609
|
const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
|
|
@@ -16734,7 +17615,7 @@ async function runSkill(opts) {
|
|
|
16734
17615
|
const target = claudeConfigPath();
|
|
16735
17616
|
let existing = {};
|
|
16736
17617
|
try {
|
|
16737
|
-
existing = JSON.parse(await
|
|
17618
|
+
existing = JSON.parse(await import_node_fs41.promises.readFile(target, "utf8"));
|
|
16738
17619
|
} catch (err) {
|
|
16739
17620
|
if (err.code !== "ENOENT") {
|
|
16740
17621
|
console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
|
|
@@ -16746,10 +17627,13 @@ async function runSkill(opts) {
|
|
|
16746
17627
|
...existing,
|
|
16747
17628
|
mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
|
|
16748
17629
|
};
|
|
16749
|
-
await
|
|
16750
|
-
await
|
|
17630
|
+
await import_node_fs41.promises.mkdir(import_node_path62.default.dirname(target), { recursive: true });
|
|
17631
|
+
await import_node_fs41.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
16751
17632
|
console.log(`neat skill: wrote mcpServers.neat to ${target}`);
|
|
16752
17633
|
console.log("restart Claude Code to pick up the new MCP server.");
|
|
17634
|
+
console.log("");
|
|
17635
|
+
console.log("Tip: run `neat hooks --apply` to also install the search-nudge hook, so");
|
|
17636
|
+
console.log("your agent reaches for the graph before it grep-scans the repo.");
|
|
16753
17637
|
return { exitCode: 0 };
|
|
16754
17638
|
}
|
|
16755
17639
|
console.log("neat skill \u2014 Claude Code MCP drop-in for NEAT");
|
|
@@ -16762,13 +17646,16 @@ async function runSkill(opts) {
|
|
|
16762
17646
|
console.log("");
|
|
16763
17647
|
console.log("The MCP server reads NEAT_CORE_URL for the daemon URL \u2014 point it at a");
|
|
16764
17648
|
console.log("non-default daemon by editing that value in the generated config.");
|
|
17649
|
+
console.log("");
|
|
17650
|
+
console.log("See also `neat hooks --apply` \u2014 the search-nudge hook + graph-first guidance");
|
|
17651
|
+
console.log("that steer an agent to query the graph before falling back to text search.");
|
|
16765
17652
|
return { exitCode: 0 };
|
|
16766
17653
|
}
|
|
16767
17654
|
async function main() {
|
|
16768
17655
|
const argv = process.argv.slice(2);
|
|
16769
17656
|
const cmd0 = argv[0];
|
|
16770
17657
|
if (cmd0 === "-h" || cmd0 === "--help") {
|
|
16771
|
-
|
|
17658
|
+
usage2();
|
|
16772
17659
|
process.exit(0);
|
|
16773
17660
|
}
|
|
16774
17661
|
if (cmd0 === "--version" || cmd0 === "-v" || cmd0 === "version") {
|
|
@@ -16780,6 +17667,11 @@ async function main() {
|
|
|
16780
17667
|
if (code !== 0) process.exit(code);
|
|
16781
17668
|
return;
|
|
16782
17669
|
}
|
|
17670
|
+
if (cmd0 === "hooks") {
|
|
17671
|
+
const code = await runHooksCommand(argv.slice(1));
|
|
17672
|
+
if (code !== 0) process.exit(code);
|
|
17673
|
+
return;
|
|
17674
|
+
}
|
|
16783
17675
|
const argvParsed = parseArgs(argv);
|
|
16784
17676
|
if (argvParsed.positional.length === 0) {
|
|
16785
17677
|
const orchestratorCode2 = await tryOrchestrator(process.cwd(), argvParsed);
|
|
@@ -16794,19 +17686,19 @@ async function main() {
|
|
|
16794
17686
|
const target = positional[0];
|
|
16795
17687
|
if (!target) {
|
|
16796
17688
|
console.error("neat init: missing <path>");
|
|
16797
|
-
|
|
17689
|
+
usage2();
|
|
16798
17690
|
process.exit(2);
|
|
16799
17691
|
}
|
|
16800
17692
|
if (apply3 && dryRun) {
|
|
16801
17693
|
console.error("neat init: --apply and --dry-run are mutually exclusive");
|
|
16802
17694
|
process.exit(2);
|
|
16803
17695
|
}
|
|
16804
|
-
const scanPath =
|
|
17696
|
+
const scanPath = import_node_path62.default.resolve(target);
|
|
16805
17697
|
const projectExplicit = parsed.project !== null;
|
|
16806
|
-
const projectName = projectExplicit ? project :
|
|
17698
|
+
const projectName = projectExplicit ? project : import_node_path62.default.basename(scanPath);
|
|
16807
17699
|
const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
|
|
16808
|
-
const fallback = pathsForProject(projectKey,
|
|
16809
|
-
const outPath =
|
|
17700
|
+
const fallback = pathsForProject(projectKey, import_node_path62.default.join(scanPath, "neat-out")).snapshotPath;
|
|
17701
|
+
const outPath = import_node_path62.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
|
|
16810
17702
|
const result = await runInit({
|
|
16811
17703
|
scanPath,
|
|
16812
17704
|
outPath,
|
|
@@ -16824,24 +17716,24 @@ async function main() {
|
|
|
16824
17716
|
const target = positional[0];
|
|
16825
17717
|
if (!target) {
|
|
16826
17718
|
console.error("neat watch: missing <path>");
|
|
16827
|
-
|
|
17719
|
+
usage2();
|
|
16828
17720
|
process.exit(2);
|
|
16829
17721
|
}
|
|
16830
|
-
const scanPath =
|
|
16831
|
-
const stat = await
|
|
17722
|
+
const scanPath = import_node_path62.default.resolve(target);
|
|
17723
|
+
const stat = await import_node_fs41.promises.stat(scanPath).catch(() => null);
|
|
16832
17724
|
if (!stat || !stat.isDirectory()) {
|
|
16833
17725
|
console.error(`neat watch: ${scanPath} is not a directory`);
|
|
16834
17726
|
process.exit(2);
|
|
16835
17727
|
}
|
|
16836
|
-
const projectPaths = pathsForProject(project,
|
|
16837
|
-
const outPath =
|
|
16838
|
-
const errorsPath =
|
|
16839
|
-
process.env.NEAT_ERRORS_PATH ??
|
|
17728
|
+
const projectPaths = pathsForProject(project, import_node_path62.default.join(scanPath, "neat-out"));
|
|
17729
|
+
const outPath = import_node_path62.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
|
|
17730
|
+
const errorsPath = import_node_path62.default.resolve(
|
|
17731
|
+
process.env.NEAT_ERRORS_PATH ?? import_node_path62.default.join(import_node_path62.default.dirname(outPath), import_node_path62.default.basename(projectPaths.errorsPath))
|
|
16840
17732
|
);
|
|
16841
|
-
const staleEventsPath =
|
|
16842
|
-
process.env.NEAT_STALE_EVENTS_PATH ??
|
|
17733
|
+
const staleEventsPath = import_node_path62.default.resolve(
|
|
17734
|
+
process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path62.default.join(import_node_path62.default.dirname(outPath), import_node_path62.default.basename(projectPaths.staleEventsPath))
|
|
16843
17735
|
);
|
|
16844
|
-
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ?
|
|
17736
|
+
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path62.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
|
|
16845
17737
|
const handle = await startWatch(getGraph(project), {
|
|
16846
17738
|
scanPath,
|
|
16847
17739
|
outPath,
|
|
@@ -16883,7 +17775,7 @@ async function main() {
|
|
|
16883
17775
|
const name = positional[0];
|
|
16884
17776
|
if (!name) {
|
|
16885
17777
|
console.error("neat pause: missing <name>");
|
|
16886
|
-
|
|
17778
|
+
usage2();
|
|
16887
17779
|
process.exit(2);
|
|
16888
17780
|
}
|
|
16889
17781
|
const daemon = await findDaemonByProject(name);
|
|
@@ -16908,7 +17800,7 @@ async function main() {
|
|
|
16908
17800
|
const name = positional[0];
|
|
16909
17801
|
if (!name) {
|
|
16910
17802
|
console.error("neat resume: missing <name>");
|
|
16911
|
-
|
|
17803
|
+
usage2();
|
|
16912
17804
|
process.exit(2);
|
|
16913
17805
|
}
|
|
16914
17806
|
const daemon = await findDaemonByProject(name);
|
|
@@ -16938,7 +17830,7 @@ async function main() {
|
|
|
16938
17830
|
const name = positional[0];
|
|
16939
17831
|
if (!name) {
|
|
16940
17832
|
console.error("neat uninstall: missing <name>");
|
|
16941
|
-
|
|
17833
|
+
usage2();
|
|
16942
17834
|
process.exit(2);
|
|
16943
17835
|
}
|
|
16944
17836
|
const daemon = await findDaemonByProject(name);
|
|
@@ -17020,15 +17912,15 @@ async function main() {
|
|
|
17020
17912
|
return;
|
|
17021
17913
|
}
|
|
17022
17914
|
console.error(`neat: unknown command "${cmd}"`);
|
|
17023
|
-
|
|
17915
|
+
usage2();
|
|
17024
17916
|
process.exit(1);
|
|
17025
17917
|
}
|
|
17026
17918
|
async function tryOrchestrator(cmd, parsed) {
|
|
17027
|
-
const scanPath =
|
|
17028
|
-
const stat = await
|
|
17919
|
+
const scanPath = import_node_path62.default.resolve(cmd);
|
|
17920
|
+
const stat = await import_node_fs41.promises.stat(scanPath).catch(() => null);
|
|
17029
17921
|
if (!stat || !stat.isDirectory()) return null;
|
|
17030
17922
|
const projectExplicit = parsed.project !== null;
|
|
17031
|
-
const projectName = projectExplicit ? parsed.project :
|
|
17923
|
+
const projectName = projectExplicit ? parsed.project : import_node_path62.default.basename(scanPath);
|
|
17032
17924
|
const result = await runOrchestrator({
|
|
17033
17925
|
scanPath,
|
|
17034
17926
|
project: projectName,
|
|
@@ -17217,10 +18109,10 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
17217
18109
|
const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
17218
18110
|
const out = [];
|
|
17219
18111
|
for (const p of parts) {
|
|
17220
|
-
const r =
|
|
18112
|
+
const r = import_types50.DivergenceTypeSchema.safeParse(p);
|
|
17221
18113
|
if (!r.success) {
|
|
17222
18114
|
console.error(
|
|
17223
|
-
`neat divergences: unknown --type "${p}". allowed: ${
|
|
18115
|
+
`neat divergences: unknown --type "${p}". allowed: ${import_types50.DivergenceTypeSchema.options.join(", ")}`
|
|
17224
18116
|
);
|
|
17225
18117
|
return 2;
|
|
17226
18118
|
}
|