@neat.is/core 0.9.15-dev.20260906 → 0.9.15-dev.20260908
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/cli.cjs +694 -200
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +16 -2
- package/dist/cli.d.ts +16 -2
- package/dist/cli.js +625 -135
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
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 path97 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
65
|
+
if (exactUnauthPaths.has(path97) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path97)) {
|
|
66
66
|
done();
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
@@ -415,8 +415,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
415
415
|
const v = attrs[key];
|
|
416
416
|
if (typeof v === "string" && v.length > 0) {
|
|
417
417
|
const q = v.indexOf("?");
|
|
418
|
-
const
|
|
419
|
-
if (
|
|
418
|
+
const path97 = q === -1 ? v : v.slice(0, q);
|
|
419
|
+
if (path97.length > 0) return path97;
|
|
420
420
|
}
|
|
421
421
|
}
|
|
422
422
|
return void 0;
|
|
@@ -787,12 +787,14 @@ __export(cli_exports, {
|
|
|
787
787
|
CLAUDE_SKILL_CONFIG: () => CLAUDE_SKILL_CONFIG,
|
|
788
788
|
ProjectResolutionError: () => ProjectResolutionError,
|
|
789
789
|
QUERY_VERBS: () => QUERY_VERBS,
|
|
790
|
+
UnknownProfileError: () => UnknownProfileError,
|
|
790
791
|
commandPrefix: () => commandPrefix,
|
|
791
792
|
isNpxInvocation: () => isNpxInvocation,
|
|
792
793
|
main: () => main,
|
|
793
794
|
parseArgs: () => parseArgs,
|
|
794
795
|
printBanner: () => printBanner,
|
|
795
796
|
readPackageVersion: () => readPackageVersion,
|
|
797
|
+
resolveClientTarget: () => resolveClientTarget,
|
|
796
798
|
resolveDaemonUrl: () => resolveDaemonUrl,
|
|
797
799
|
resolveProjectForVerb: () => resolveProjectForVerb,
|
|
798
800
|
runInit: () => runInit,
|
|
@@ -803,9 +805,9 @@ __export(cli_exports, {
|
|
|
803
805
|
});
|
|
804
806
|
module.exports = __toCommonJS(cli_exports);
|
|
805
807
|
init_cjs_shims();
|
|
806
|
-
var
|
|
807
|
-
var
|
|
808
|
-
var
|
|
808
|
+
var import_node_path96 = __toESM(require("path"), 1);
|
|
809
|
+
var import_node_os10 = __toESM(require("os"), 1);
|
|
810
|
+
var import_node_fs59 = require("fs");
|
|
809
811
|
|
|
810
812
|
// src/banner.ts
|
|
811
813
|
init_cjs_shims();
|
|
@@ -1382,19 +1384,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1382
1384
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1383
1385
|
let best = { path: [start], edges: [] };
|
|
1384
1386
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1385
|
-
function step(node,
|
|
1386
|
-
if (
|
|
1387
|
-
best = { path: [...
|
|
1387
|
+
function step(node, path97, edges) {
|
|
1388
|
+
if (path97.length > best.path.length) {
|
|
1389
|
+
best = { path: [...path97], edges: [...edges] };
|
|
1388
1390
|
}
|
|
1389
|
-
if (
|
|
1391
|
+
if (path97.length - 1 >= maxDepth) return;
|
|
1390
1392
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1391
1393
|
for (const [srcId, edge] of incoming) {
|
|
1392
1394
|
if (visited.has(srcId)) continue;
|
|
1393
1395
|
visited.add(srcId);
|
|
1394
|
-
|
|
1396
|
+
path97.push(srcId);
|
|
1395
1397
|
edges.push(edge);
|
|
1396
|
-
step(srcId,
|
|
1397
|
-
|
|
1398
|
+
step(srcId, path97, edges);
|
|
1399
|
+
path97.pop();
|
|
1398
1400
|
edges.pop();
|
|
1399
1401
|
visited.delete(srcId);
|
|
1400
1402
|
}
|
|
@@ -1609,20 +1611,20 @@ function dominantFailingCall(graph, serviceId17, visited) {
|
|
|
1609
1611
|
return best;
|
|
1610
1612
|
}
|
|
1611
1613
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1612
|
-
const
|
|
1614
|
+
const path97 = [originServiceId];
|
|
1613
1615
|
const edges = [];
|
|
1614
1616
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1615
1617
|
let current = originServiceId;
|
|
1616
1618
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1617
1619
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1618
1620
|
if (!hop) break;
|
|
1619
|
-
|
|
1621
|
+
path97.push(hop.nextService);
|
|
1620
1622
|
edges.push(hop.edge);
|
|
1621
1623
|
visited.add(hop.nextService);
|
|
1622
1624
|
current = hop.nextService;
|
|
1623
1625
|
}
|
|
1624
1626
|
if (edges.length === 0) return null;
|
|
1625
|
-
return { path:
|
|
1627
|
+
return { path: path97, edges, culprit: current };
|
|
1626
1628
|
}
|
|
1627
1629
|
function isStaleCallEdge(e) {
|
|
1628
1630
|
return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
|
|
@@ -1658,26 +1660,26 @@ function dominantStaleCall(graph, serviceId17, visited) {
|
|
|
1658
1660
|
return best;
|
|
1659
1661
|
}
|
|
1660
1662
|
function followStaleCallChain(graph, originServiceId, maxDepth) {
|
|
1661
|
-
const
|
|
1663
|
+
const path97 = [originServiceId];
|
|
1662
1664
|
const edges = [];
|
|
1663
1665
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1664
1666
|
let current = originServiceId;
|
|
1665
1667
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1666
1668
|
const hop = dominantStaleCall(graph, current, visited);
|
|
1667
1669
|
if (!hop) break;
|
|
1668
|
-
|
|
1670
|
+
path97.push(hop.nextService);
|
|
1669
1671
|
edges.push(hop.edge);
|
|
1670
1672
|
visited.add(hop.nextService);
|
|
1671
1673
|
current = hop.nextService;
|
|
1672
1674
|
}
|
|
1673
1675
|
if (edges.length === 0) return null;
|
|
1674
|
-
return { path:
|
|
1676
|
+
return { path: path97, edges, culprit: current };
|
|
1675
1677
|
}
|
|
1676
1678
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1677
1679
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1678
1680
|
if (!chain) return null;
|
|
1679
1681
|
const culprit = chain.culprit;
|
|
1680
|
-
const
|
|
1682
|
+
const path97 = [...chain.path];
|
|
1681
1683
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1682
1684
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1683
1685
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1685,14 +1687,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1685
1687
|
if (loc) {
|
|
1686
1688
|
let rootCauseNode = culprit;
|
|
1687
1689
|
if (loc.fileNode) {
|
|
1688
|
-
|
|
1690
|
+
path97.push(loc.fileNode);
|
|
1689
1691
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1690
1692
|
rootCauseNode = loc.fileNode;
|
|
1691
1693
|
}
|
|
1692
1694
|
return import_types.RootCauseResultSchema.parse({
|
|
1693
1695
|
rootCauseNode,
|
|
1694
1696
|
rootCauseReason: loc.rootCauseReason,
|
|
1695
|
-
traversalPath:
|
|
1697
|
+
traversalPath: path97,
|
|
1696
1698
|
edgeProvenances,
|
|
1697
1699
|
confidence,
|
|
1698
1700
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1704,7 +1706,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1704
1706
|
return import_types.RootCauseResultSchema.parse({
|
|
1705
1707
|
rootCauseNode: culprit,
|
|
1706
1708
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1707
|
-
traversalPath:
|
|
1709
|
+
traversalPath: path97,
|
|
1708
1710
|
edgeProvenances,
|
|
1709
1711
|
confidence,
|
|
1710
1712
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2357,10 +2359,10 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
|
2357
2359
|
traversalPath = staleChain.path;
|
|
2358
2360
|
edgeProvenances = staleChain.edges.map((e) => e.provenance);
|
|
2359
2361
|
} else if (top.node !== seedNode) {
|
|
2360
|
-
const
|
|
2361
|
-
if (
|
|
2362
|
-
traversalPath =
|
|
2363
|
-
edgeProvenances =
|
|
2362
|
+
const path97 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
|
|
2363
|
+
if (path97) {
|
|
2364
|
+
traversalPath = path97.nodes;
|
|
2365
|
+
edgeProvenances = path97.edges.map((e) => e.provenance);
|
|
2364
2366
|
} else {
|
|
2365
2367
|
traversalPath = [errorNodeId, top.node];
|
|
2366
2368
|
edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
|
|
@@ -3811,8 +3813,8 @@ function chiRoutesFromSource(source, parser) {
|
|
|
3811
3813
|
chiWalk(tree.rootNode, "", out);
|
|
3812
3814
|
return out;
|
|
3813
3815
|
}
|
|
3814
|
-
function stripChiRegex(
|
|
3815
|
-
return
|
|
3816
|
+
function stripChiRegex(path97) {
|
|
3817
|
+
return path97.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
|
|
3816
3818
|
}
|
|
3817
3819
|
function chiWalk(node, prefix, out) {
|
|
3818
3820
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
@@ -4484,9 +4486,9 @@ function rubyRocketRoute(args) {
|
|
|
4484
4486
|
if (!pair || pair.type !== "pair") continue;
|
|
4485
4487
|
const k = pair.childForFieldName("key");
|
|
4486
4488
|
if (k?.type !== "string") continue;
|
|
4487
|
-
const
|
|
4488
|
-
if (
|
|
4489
|
-
return { path:
|
|
4489
|
+
const path97 = rubyLiteral(k);
|
|
4490
|
+
if (path97 === null) continue;
|
|
4491
|
+
return { path: path97, target: rubyLiteral(pair.childForFieldName("value")) };
|
|
4490
4492
|
}
|
|
4491
4493
|
return null;
|
|
4492
4494
|
}
|
|
@@ -19899,10 +19901,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
19899
19901
|
// src/connectors/supabase/map.ts
|
|
19900
19902
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
19901
19903
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
19902
|
-
function targetFromRestPath(
|
|
19903
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
19904
|
+
function targetFromRestPath(path97) {
|
|
19905
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path97);
|
|
19904
19906
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
19905
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
19907
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path97);
|
|
19906
19908
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
19907
19909
|
return null;
|
|
19908
19910
|
}
|
|
@@ -20506,9 +20508,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
20506
20508
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
20507
20509
|
if (secondSep === -1) return null;
|
|
20508
20510
|
const method = rest.slice(0, secondSep);
|
|
20509
|
-
const
|
|
20510
|
-
if (!resourceName || !method || !
|
|
20511
|
-
return { resourceName, method, path:
|
|
20511
|
+
const path97 = rest.slice(secondSep + 1);
|
|
20512
|
+
if (!resourceName || !method || !path97) return null;
|
|
20513
|
+
return { resourceName, method, path: path97 };
|
|
20512
20514
|
}
|
|
20513
20515
|
function resourceNameFor(type, labels) {
|
|
20514
20516
|
if (!labels) return null;
|
|
@@ -20546,14 +20548,14 @@ function mapLogEntryToSignal(entry2) {
|
|
|
20546
20548
|
if (!req) return null;
|
|
20547
20549
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
20548
20550
|
const method = req.requestMethod.toUpperCase();
|
|
20549
|
-
const
|
|
20550
|
-
if (
|
|
20551
|
+
const path97 = pathFromRequestUrl(req.requestUrl);
|
|
20552
|
+
if (path97 === null) return null;
|
|
20551
20553
|
const timestamp = entry2.timestamp;
|
|
20552
20554
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
20553
20555
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
20554
20556
|
return {
|
|
20555
20557
|
targetKind: resourceType,
|
|
20556
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
20558
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path97 }),
|
|
20557
20559
|
callCount: 1,
|
|
20558
20560
|
errorCount: isError ? 1 : 0,
|
|
20559
20561
|
lastObservedIso: timestamp
|
|
@@ -20760,7 +20762,7 @@ function mapEventToSignal(event) {
|
|
|
20760
20762
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
20761
20763
|
const statusCode = metadata?.statusCode;
|
|
20762
20764
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
20763
|
-
const
|
|
20765
|
+
const path97 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
20764
20766
|
return {
|
|
20765
20767
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
20766
20768
|
targetName: scriptName,
|
|
@@ -20768,7 +20770,7 @@ function mapEventToSignal(event) {
|
|
|
20768
20770
|
errorCount: isError ? 1 : 0,
|
|
20769
20771
|
lastObservedIso: observedAt.toISOString(),
|
|
20770
20772
|
method,
|
|
20771
|
-
...
|
|
20773
|
+
...path97 ? { path: path97 } : {},
|
|
20772
20774
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
20773
20775
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
20774
20776
|
};
|
|
@@ -20814,8 +20816,8 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
20814
20816
|
});
|
|
20815
20817
|
return found;
|
|
20816
20818
|
}
|
|
20817
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
20818
|
-
const normalizedPath = normalizePathTemplate(
|
|
20819
|
+
function findMatchingRouteNode(graph, serviceName, method, path97) {
|
|
20820
|
+
const normalizedPath = normalizePathTemplate(path97);
|
|
20819
20821
|
let found = null;
|
|
20820
20822
|
graph.forEachNode((id, attrs) => {
|
|
20821
20823
|
if (found) return;
|
|
@@ -20832,10 +20834,10 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
20832
20834
|
return (signal) => {
|
|
20833
20835
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
20834
20836
|
const scriptName = signal.targetName;
|
|
20835
|
-
const { method, path:
|
|
20837
|
+
const { method, path: path97 } = signal;
|
|
20836
20838
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
20837
|
-
if (!method || !
|
|
20838
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
20839
|
+
if (!method || !path97) return wholeFileId;
|
|
20840
|
+
return findMatchingRouteNode(graph, serviceName, method, path97) ?? wholeFileId;
|
|
20839
20841
|
};
|
|
20840
20842
|
const mapping = config.workers?.[scriptName];
|
|
20841
20843
|
if (mapping) {
|
|
@@ -21187,9 +21189,9 @@ function parseCloudRunTargetName(targetName) {
|
|
|
21187
21189
|
const secondSep = rest.indexOf(FIELD_SEP2);
|
|
21188
21190
|
if (secondSep === -1) return null;
|
|
21189
21191
|
const method = rest.slice(0, secondSep);
|
|
21190
|
-
const
|
|
21191
|
-
if (!serviceName || !method || !
|
|
21192
|
-
return { serviceName, method, path:
|
|
21192
|
+
const path97 = rest.slice(secondSep + 1);
|
|
21193
|
+
if (!serviceName || !method || !path97) return null;
|
|
21194
|
+
return { serviceName, method, path: path97 };
|
|
21193
21195
|
}
|
|
21194
21196
|
|
|
21195
21197
|
// src/connectors/cloud-run/map.ts
|
|
@@ -21218,14 +21220,14 @@ function mapLogEntryToSignal2(entry2) {
|
|
|
21218
21220
|
if (!req) return null;
|
|
21219
21221
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
21220
21222
|
const method = req.requestMethod.toUpperCase();
|
|
21221
|
-
const
|
|
21222
|
-
if (
|
|
21223
|
+
const path97 = pathFromRequestUrl2(req.requestUrl);
|
|
21224
|
+
if (path97 === null) return null;
|
|
21223
21225
|
const timestamp = entry2.timestamp;
|
|
21224
21226
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
21225
21227
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
|
|
21226
21228
|
return {
|
|
21227
21229
|
targetKind: CLOUD_RUN_TARGET_KIND,
|
|
21228
|
-
targetName: packCloudRunTargetName({ serviceName, method, path:
|
|
21230
|
+
targetName: packCloudRunTargetName({ serviceName, method, path: path97 }),
|
|
21229
21231
|
callCount: 1,
|
|
21230
21232
|
errorCount: isError ? 1 : 0,
|
|
21231
21233
|
lastObservedIso: timestamp
|
|
@@ -21264,14 +21266,14 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
21264
21266
|
if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
|
|
21265
21267
|
const identity = parseCloudRunTargetName(signal.targetName);
|
|
21266
21268
|
if (!identity) return null;
|
|
21267
|
-
const { serviceName: gcpServiceName, method, path:
|
|
21269
|
+
const { serviceName: gcpServiceName, method, path: path97 } = identity;
|
|
21268
21270
|
const mappedService = config.serviceMap?.[gcpServiceName];
|
|
21269
21271
|
if (mappedService) {
|
|
21270
21272
|
const routeNodeId = findMatchingRouteNode2(
|
|
21271
21273
|
graph,
|
|
21272
21274
|
mappedService,
|
|
21273
21275
|
method,
|
|
21274
|
-
normalizePathTemplate(
|
|
21276
|
+
normalizePathTemplate(path97)
|
|
21275
21277
|
);
|
|
21276
21278
|
if (routeNodeId) {
|
|
21277
21279
|
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types82.EdgeType.CALLS };
|
|
@@ -21407,9 +21409,9 @@ function parseGcpLbTargetName(targetName) {
|
|
|
21407
21409
|
const secondSep = rest.indexOf(FIELD_SEP3);
|
|
21408
21410
|
if (secondSep === -1) return null;
|
|
21409
21411
|
const method = rest.slice(0, secondSep);
|
|
21410
|
-
const
|
|
21411
|
-
if (!backendServiceName || !method || !
|
|
21412
|
-
return { backendServiceName, method, path:
|
|
21412
|
+
const path97 = rest.slice(secondSep + 1);
|
|
21413
|
+
if (!backendServiceName || !method || !path97) return null;
|
|
21414
|
+
return { backendServiceName, method, path: path97 };
|
|
21413
21415
|
}
|
|
21414
21416
|
|
|
21415
21417
|
// src/connectors/gcp-lb/map.ts
|
|
@@ -21438,14 +21440,14 @@ function mapLogEntryToSignal3(entry2) {
|
|
|
21438
21440
|
if (!req) return null;
|
|
21439
21441
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
21440
21442
|
const method = req.requestMethod.toUpperCase();
|
|
21441
|
-
const
|
|
21442
|
-
if (
|
|
21443
|
+
const path97 = pathFromRequestUrl3(req.requestUrl);
|
|
21444
|
+
if (path97 === null) return null;
|
|
21443
21445
|
const timestamp = entry2.timestamp;
|
|
21444
21446
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
21445
21447
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD5;
|
|
21446
21448
|
return {
|
|
21447
21449
|
targetKind: GCP_LB_TARGET_KIND,
|
|
21448
|
-
targetName: packGcpLbTargetName({ backendServiceName, method, path:
|
|
21450
|
+
targetName: packGcpLbTargetName({ backendServiceName, method, path: path97 }),
|
|
21449
21451
|
callCount: 1,
|
|
21450
21452
|
errorCount: isError ? 1 : 0,
|
|
21451
21453
|
lastObservedIso: timestamp
|
|
@@ -21484,14 +21486,14 @@ function createGcpLbResolveTarget(graph, config) {
|
|
|
21484
21486
|
if (signal.targetKind !== GCP_LB_TARGET_KIND) return null;
|
|
21485
21487
|
const identity = parseGcpLbTargetName(signal.targetName);
|
|
21486
21488
|
if (!identity) return null;
|
|
21487
|
-
const { backendServiceName, method, path:
|
|
21489
|
+
const { backendServiceName, method, path: path97 } = identity;
|
|
21488
21490
|
const mappedService = config.backendServiceMap?.[backendServiceName];
|
|
21489
21491
|
if (mappedService) {
|
|
21490
21492
|
const routeNodeId = findMatchingRouteNode3(
|
|
21491
21493
|
graph,
|
|
21492
21494
|
mappedService,
|
|
21493
21495
|
method,
|
|
21494
|
-
normalizePathTemplate(
|
|
21496
|
+
normalizePathTemplate(path97)
|
|
21495
21497
|
);
|
|
21496
21498
|
if (routeNodeId) {
|
|
21497
21499
|
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types86.EdgeType.CALLS };
|
|
@@ -23856,9 +23858,9 @@ function makeK8sFetchImpl(transport) {
|
|
|
23856
23858
|
req.end();
|
|
23857
23859
|
}));
|
|
23858
23860
|
}
|
|
23859
|
-
async function listResource(transport, namespace,
|
|
23861
|
+
async function listResource(transport, namespace, path97, opts = {}) {
|
|
23860
23862
|
const base = opts.apiUrl ?? transport.server;
|
|
23861
|
-
const url = `${base.replace(/\/$/, "")}${
|
|
23863
|
+
const url = `${base.replace(/\/$/, "")}${path97}`;
|
|
23862
23864
|
const fetchImpl = opts.fetchImpl ?? makeK8sFetchImpl(transport);
|
|
23863
23865
|
const res = await junctionFetch(
|
|
23864
23866
|
url,
|
|
@@ -23868,7 +23870,7 @@ async function listResource(transport, namespace, path96, opts = {}) {
|
|
|
23868
23870
|
{ provider: "kubernetes", accountKey: `${safeHost(transport.server)}/${namespace}`, fetchImpl }
|
|
23869
23871
|
);
|
|
23870
23872
|
if (!res.ok) {
|
|
23871
|
-
throw new Error(`kubernetes ${
|
|
23873
|
+
throw new Error(`kubernetes ${path97} failed: ${res.status} ${res.statusText}`);
|
|
23872
23874
|
}
|
|
23873
23875
|
const json = await res.json();
|
|
23874
23876
|
return Array.isArray(json.items) ? json.items : [];
|
|
@@ -29341,10 +29343,10 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
29341
29343
|
const root = baseUrl.replace(/\/$/, "");
|
|
29342
29344
|
const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
|
|
29343
29345
|
return {
|
|
29344
|
-
async get(
|
|
29346
|
+
async get(path97) {
|
|
29345
29347
|
let res;
|
|
29346
29348
|
try {
|
|
29347
|
-
res = await fetch(`${root}${
|
|
29349
|
+
res = await fetch(`${root}${path97}`, {
|
|
29348
29350
|
headers: { ...authHeader }
|
|
29349
29351
|
});
|
|
29350
29352
|
} catch (err) {
|
|
@@ -29356,16 +29358,16 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
29356
29358
|
const body = await res.text().catch(() => "");
|
|
29357
29359
|
throw new HttpError(
|
|
29358
29360
|
res.status,
|
|
29359
|
-
`${res.status} ${res.statusText} on GET ${
|
|
29361
|
+
`${res.status} ${res.statusText} on GET ${path97}: ${body}`,
|
|
29360
29362
|
body
|
|
29361
29363
|
);
|
|
29362
29364
|
}
|
|
29363
29365
|
return await res.json();
|
|
29364
29366
|
},
|
|
29365
|
-
async post(
|
|
29367
|
+
async post(path97, body) {
|
|
29366
29368
|
let res;
|
|
29367
29369
|
try {
|
|
29368
|
-
res = await fetch(`${root}${
|
|
29370
|
+
res = await fetch(`${root}${path97}`, {
|
|
29369
29371
|
method: "POST",
|
|
29370
29372
|
headers: { "content-type": "application/json", ...authHeader },
|
|
29371
29373
|
body: JSON.stringify(body)
|
|
@@ -29379,7 +29381,7 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
29379
29381
|
const text = await res.text().catch(() => "");
|
|
29380
29382
|
throw new HttpError(
|
|
29381
29383
|
res.status,
|
|
29382
|
-
`${res.status} ${res.statusText} on POST ${
|
|
29384
|
+
`${res.status} ${res.statusText} on POST ${path97}: ${text}`,
|
|
29383
29385
|
text
|
|
29384
29386
|
);
|
|
29385
29387
|
}
|
|
@@ -29393,12 +29395,12 @@ function projectPath(project, suffix) {
|
|
|
29393
29395
|
}
|
|
29394
29396
|
async function runRootCause(client, input) {
|
|
29395
29397
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
29396
|
-
const
|
|
29398
|
+
const path97 = projectPath(
|
|
29397
29399
|
input.project,
|
|
29398
29400
|
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
29399
29401
|
);
|
|
29400
29402
|
try {
|
|
29401
|
-
const result = await client.get(
|
|
29403
|
+
const result = await client.get(path97);
|
|
29402
29404
|
const arrowPath = result.traversalPath.join(" \u2190 ");
|
|
29403
29405
|
const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
|
|
29404
29406
|
const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
|
|
@@ -29424,12 +29426,12 @@ async function runRootCause(client, input) {
|
|
|
29424
29426
|
}
|
|
29425
29427
|
async function runBlastRadius(client, input) {
|
|
29426
29428
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
29427
|
-
const
|
|
29429
|
+
const path97 = projectPath(
|
|
29428
29430
|
input.project,
|
|
29429
29431
|
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
29430
29432
|
);
|
|
29431
29433
|
try {
|
|
29432
|
-
const result = await client.get(
|
|
29434
|
+
const result = await client.get(path97);
|
|
29433
29435
|
if (result.totalAffected === 0) {
|
|
29434
29436
|
return {
|
|
29435
29437
|
summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
|
|
@@ -29463,12 +29465,12 @@ function formatBlastEntry(n) {
|
|
|
29463
29465
|
}
|
|
29464
29466
|
async function runDependencies(client, input) {
|
|
29465
29467
|
const depth = input.depth ?? 3;
|
|
29466
|
-
const
|
|
29468
|
+
const path97 = projectPath(
|
|
29467
29469
|
input.project,
|
|
29468
29470
|
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
29469
29471
|
);
|
|
29470
29472
|
try {
|
|
29471
|
-
const result = await client.get(
|
|
29473
|
+
const result = await client.get(path97);
|
|
29472
29474
|
if (result.total === 0) {
|
|
29473
29475
|
return {
|
|
29474
29476
|
summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
|
|
@@ -29560,9 +29562,9 @@ function formatDuration(ms) {
|
|
|
29560
29562
|
return `${Math.round(h / 24)}d`;
|
|
29561
29563
|
}
|
|
29562
29564
|
async function runIncidents(client, input) {
|
|
29563
|
-
const
|
|
29565
|
+
const path97 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
29564
29566
|
try {
|
|
29565
|
-
const body = await client.get(
|
|
29567
|
+
const body = await client.get(path97);
|
|
29566
29568
|
const events = body.events;
|
|
29567
29569
|
if (events.length === 0) {
|
|
29568
29570
|
return {
|
|
@@ -30033,29 +30035,465 @@ async function runDoctorCommand(argv, deps = {}) {
|
|
|
30033
30035
|
return checks.every((c) => c.ok) ? 0 : 1;
|
|
30034
30036
|
}
|
|
30035
30037
|
|
|
30036
|
-
// src/
|
|
30038
|
+
// src/login-cli.ts
|
|
30039
|
+
init_cjs_shims();
|
|
30040
|
+
var import_promises2 = __toESM(require("readline/promises"), 1);
|
|
30041
|
+
|
|
30042
|
+
// src/profiles.ts
|
|
30037
30043
|
init_cjs_shims();
|
|
30038
|
-
var import_node_path90 = __toESM(require("path"), 1);
|
|
30039
|
-
var import_node_os6 = __toESM(require("os"), 1);
|
|
30040
30044
|
var import_node_fs54 = require("fs");
|
|
30045
|
+
var import_node_os6 = __toESM(require("os"), 1);
|
|
30046
|
+
var import_node_path90 = __toESM(require("path"), 1);
|
|
30047
|
+
var PROFILES_CONFIG_VERSION = 1;
|
|
30048
|
+
function neatHome3() {
|
|
30049
|
+
const override = process.env.NEAT_HOME;
|
|
30050
|
+
if (override && override.length > 0) return import_node_path90.default.resolve(override);
|
|
30051
|
+
return import_node_path90.default.join(import_node_os6.default.homedir(), ".neat");
|
|
30052
|
+
}
|
|
30053
|
+
function profilesConfigPath(home = neatHome3()) {
|
|
30054
|
+
return import_node_path90.default.join(home, "profiles.json");
|
|
30055
|
+
}
|
|
30056
|
+
function profilesConfigLockPath(home = neatHome3()) {
|
|
30057
|
+
return import_node_path90.default.join(home, "profiles.json.lock");
|
|
30058
|
+
}
|
|
30059
|
+
var MODE_MASK_LOOSER_THAN_06002 = 63;
|
|
30060
|
+
async function warnIfModeLooserThan06002(file) {
|
|
30061
|
+
if (process.platform === "win32") return;
|
|
30062
|
+
try {
|
|
30063
|
+
const stat = await import_node_fs54.promises.stat(file);
|
|
30064
|
+
if ((stat.mode & MODE_MASK_LOOSER_THAN_06002) !== 0) {
|
|
30065
|
+
const mode = (stat.mode & 511).toString(8).padStart(3, "0");
|
|
30066
|
+
console.warn(
|
|
30067
|
+
`[neat] ${file} is mode 0${mode}, looser than the 0600 this file's token calls for \u2014 run \`chmod 600 ${file}\``
|
|
30068
|
+
);
|
|
30069
|
+
}
|
|
30070
|
+
} catch {
|
|
30071
|
+
}
|
|
30072
|
+
}
|
|
30073
|
+
async function readProfilesConfig(home = neatHome3()) {
|
|
30074
|
+
const file = profilesConfigPath(home);
|
|
30075
|
+
let raw;
|
|
30076
|
+
try {
|
|
30077
|
+
raw = await import_node_fs54.promises.readFile(file, "utf8");
|
|
30078
|
+
} catch (err) {
|
|
30079
|
+
if (err.code === "ENOENT") {
|
|
30080
|
+
return { version: PROFILES_CONFIG_VERSION, profiles: [] };
|
|
30081
|
+
}
|
|
30082
|
+
throw err;
|
|
30083
|
+
}
|
|
30084
|
+
await warnIfModeLooserThan06002(file);
|
|
30085
|
+
let parsed;
|
|
30086
|
+
try {
|
|
30087
|
+
parsed = JSON.parse(raw);
|
|
30088
|
+
} catch (err) {
|
|
30089
|
+
throw new Error(`${file} is not valid JSON: ${err.message}`);
|
|
30090
|
+
}
|
|
30091
|
+
return validateConfig2(parsed, file);
|
|
30092
|
+
}
|
|
30093
|
+
function validateConfig2(parsed, file) {
|
|
30094
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
30095
|
+
throw new Error(`${file} must be a JSON object with a "profiles" array`);
|
|
30096
|
+
}
|
|
30097
|
+
const obj = parsed;
|
|
30098
|
+
const version = obj.version === void 0 ? PROFILES_CONFIG_VERSION : obj.version;
|
|
30099
|
+
if (typeof version !== "number" || !Number.isInteger(version)) {
|
|
30100
|
+
throw new Error(`${file}: "version" must be an integer`);
|
|
30101
|
+
}
|
|
30102
|
+
const rawProfiles = obj.profiles;
|
|
30103
|
+
if (!Array.isArray(rawProfiles)) {
|
|
30104
|
+
throw new Error(`${file}: "profiles" must be an array`);
|
|
30105
|
+
}
|
|
30106
|
+
const profiles = rawProfiles.map((entry2, i) => validateEntry2(entry2, i, file));
|
|
30107
|
+
const seen = /* @__PURE__ */ new Set();
|
|
30108
|
+
for (const p of profiles) {
|
|
30109
|
+
if (seen.has(p.name)) throw new Error(`${file}: duplicate profile name "${p.name}"`);
|
|
30110
|
+
seen.add(p.name);
|
|
30111
|
+
}
|
|
30112
|
+
let active;
|
|
30113
|
+
if (obj.active !== void 0) {
|
|
30114
|
+
if (typeof obj.active !== "string" || obj.active.length === 0) {
|
|
30115
|
+
throw new Error(`${file}: "active" must be a non-empty string when present`);
|
|
30116
|
+
}
|
|
30117
|
+
active = seen.has(obj.active) ? obj.active : void 0;
|
|
30118
|
+
}
|
|
30119
|
+
return { version, ...active ? { active } : {}, profiles };
|
|
30120
|
+
}
|
|
30121
|
+
function validateEntry2(entry2, index, file) {
|
|
30122
|
+
const where = `${file}: profiles[${index}]`;
|
|
30123
|
+
if (typeof entry2 !== "object" || entry2 === null || Array.isArray(entry2)) {
|
|
30124
|
+
throw new Error(`${where} must be an object`);
|
|
30125
|
+
}
|
|
30126
|
+
const e = entry2;
|
|
30127
|
+
const name = e.name;
|
|
30128
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
30129
|
+
throw new Error(`${where}.name must be a non-empty string`);
|
|
30130
|
+
}
|
|
30131
|
+
const endpoint2 = e.endpoint;
|
|
30132
|
+
if (typeof endpoint2 !== "string" || endpoint2.length === 0) {
|
|
30133
|
+
throw new Error(`${where}.endpoint must be a non-empty string`);
|
|
30134
|
+
}
|
|
30135
|
+
let parsedUrl;
|
|
30136
|
+
try {
|
|
30137
|
+
parsedUrl = new URL(endpoint2);
|
|
30138
|
+
} catch {
|
|
30139
|
+
throw new Error(`${where}.endpoint must be an absolute URL (got "${endpoint2}")`);
|
|
30140
|
+
}
|
|
30141
|
+
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
|
|
30142
|
+
throw new Error(`${where}.endpoint must be an http(s) URL (got "${parsedUrl.protocol}")`);
|
|
30143
|
+
}
|
|
30144
|
+
if (e.authToken !== void 0 && (typeof e.authToken !== "string" || e.authToken.length === 0)) {
|
|
30145
|
+
throw new Error(`${where}.authToken must be a non-empty string when present`);
|
|
30146
|
+
}
|
|
30147
|
+
return {
|
|
30148
|
+
name,
|
|
30149
|
+
endpoint: endpoint2,
|
|
30150
|
+
...typeof e.authToken === "string" ? { authToken: e.authToken } : {}
|
|
30151
|
+
};
|
|
30152
|
+
}
|
|
30153
|
+
async function resolveProfile(name, home = neatHome3()) {
|
|
30154
|
+
const { profiles } = await readProfilesConfig(home);
|
|
30155
|
+
return profiles.find((p) => p.name === name);
|
|
30156
|
+
}
|
|
30157
|
+
async function getActiveProfile(home = neatHome3()) {
|
|
30158
|
+
const { active, profiles } = await readProfilesConfig(home);
|
|
30159
|
+
if (!active) return void 0;
|
|
30160
|
+
return profiles.find((p) => p.name === active);
|
|
30161
|
+
}
|
|
30162
|
+
function serialize(config) {
|
|
30163
|
+
const names = new Set(config.profiles.map((p) => p.name));
|
|
30164
|
+
const active = config.active && names.has(config.active) ? config.active : void 0;
|
|
30165
|
+
const out = {
|
|
30166
|
+
version: config.version ?? PROFILES_CONFIG_VERSION,
|
|
30167
|
+
...active ? { active } : {},
|
|
30168
|
+
profiles: config.profiles
|
|
30169
|
+
};
|
|
30170
|
+
return `${JSON.stringify(out, null, 2)}
|
|
30171
|
+
`;
|
|
30172
|
+
}
|
|
30173
|
+
async function writeConfigAtomic(config, home) {
|
|
30174
|
+
const file = profilesConfigPath(home);
|
|
30175
|
+
await import_node_fs54.promises.mkdir(import_node_path90.default.dirname(file), { recursive: true });
|
|
30176
|
+
const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
30177
|
+
const fd = await import_node_fs54.promises.open(tmp, "w", 384);
|
|
30178
|
+
try {
|
|
30179
|
+
await fd.writeFile(serialize(config), "utf8");
|
|
30180
|
+
await fd.sync();
|
|
30181
|
+
} finally {
|
|
30182
|
+
await fd.close();
|
|
30183
|
+
}
|
|
30184
|
+
await import_node_fs54.promises.rename(tmp, file);
|
|
30185
|
+
}
|
|
30186
|
+
var LOCK_RETRY_MS2 = 50;
|
|
30187
|
+
var LOCK_TIMEOUT_MS2 = 5e3;
|
|
30188
|
+
async function acquireLock2(lockPath) {
|
|
30189
|
+
await import_node_fs54.promises.mkdir(import_node_path90.default.dirname(lockPath), { recursive: true });
|
|
30190
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS2;
|
|
30191
|
+
for (; ; ) {
|
|
30192
|
+
try {
|
|
30193
|
+
const fd = await import_node_fs54.promises.open(lockPath, "wx");
|
|
30194
|
+
await fd.writeFile(`${process.pid}
|
|
30195
|
+
`, "utf8");
|
|
30196
|
+
await fd.close();
|
|
30197
|
+
return;
|
|
30198
|
+
} catch (err) {
|
|
30199
|
+
if (err.code !== "EEXIST") throw err;
|
|
30200
|
+
if (Date.now() >= deadline) {
|
|
30201
|
+
throw new Error(
|
|
30202
|
+
`timed out acquiring ${lockPath} after ${LOCK_TIMEOUT_MS2}ms \u2014 if no other neat process is running, remove the stale lock file`
|
|
30203
|
+
);
|
|
30204
|
+
}
|
|
30205
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS2));
|
|
30206
|
+
}
|
|
30207
|
+
}
|
|
30208
|
+
}
|
|
30209
|
+
async function releaseLock2(lockPath) {
|
|
30210
|
+
await import_node_fs54.promises.rm(lockPath, { force: true });
|
|
30211
|
+
}
|
|
30212
|
+
async function withProfilesLock(home, fn) {
|
|
30213
|
+
const lockPath = profilesConfigLockPath(home);
|
|
30214
|
+
await acquireLock2(lockPath);
|
|
30215
|
+
try {
|
|
30216
|
+
return await fn();
|
|
30217
|
+
} finally {
|
|
30218
|
+
await releaseLock2(lockPath);
|
|
30219
|
+
}
|
|
30220
|
+
}
|
|
30221
|
+
async function upsertProfile(profile, opts = {}) {
|
|
30222
|
+
const home = opts.home ?? neatHome3();
|
|
30223
|
+
const validated = validateEntry2(profile, 0, profilesConfigPath(home));
|
|
30224
|
+
await withProfilesLock(home, async () => {
|
|
30225
|
+
const config = await readProfilesConfig(home);
|
|
30226
|
+
const others = config.profiles.filter((p) => p.name !== validated.name);
|
|
30227
|
+
const profiles = [...others, validated];
|
|
30228
|
+
const makeActive = opts.makeActive ?? config.profiles.length === 0;
|
|
30229
|
+
const active = makeActive ? validated.name : config.active;
|
|
30230
|
+
await writeConfigAtomic({ version: config.version, ...active ? { active } : {}, profiles }, home);
|
|
30231
|
+
});
|
|
30232
|
+
}
|
|
30233
|
+
async function removeProfile(name, home = neatHome3()) {
|
|
30234
|
+
return withProfilesLock(home, async () => {
|
|
30235
|
+
const config = await readProfilesConfig(home);
|
|
30236
|
+
const profiles = config.profiles.filter((p) => p.name !== name);
|
|
30237
|
+
if (profiles.length === config.profiles.length) return false;
|
|
30238
|
+
const active = config.active === name ? void 0 : config.active;
|
|
30239
|
+
await writeConfigAtomic({ version: config.version, ...active ? { active } : {}, profiles }, home);
|
|
30240
|
+
return true;
|
|
30241
|
+
});
|
|
30242
|
+
}
|
|
30243
|
+
async function clearActiveProfile(home = neatHome3()) {
|
|
30244
|
+
return withProfilesLock(home, async () => {
|
|
30245
|
+
const config = await readProfilesConfig(home);
|
|
30246
|
+
if (!config.active) return false;
|
|
30247
|
+
await writeConfigAtomic({ version: config.version, profiles: config.profiles }, home);
|
|
30248
|
+
return true;
|
|
30249
|
+
});
|
|
30250
|
+
}
|
|
30251
|
+
|
|
30252
|
+
// src/login-cli.ts
|
|
30253
|
+
var HEALTH_TIMEOUT_MS2 = 5e3;
|
|
30254
|
+
var DEFAULT_PROFILE_NAME = "hosted";
|
|
30255
|
+
function readFlagValue(argv, i) {
|
|
30256
|
+
const arg = argv[i];
|
|
30257
|
+
const eq = arg.indexOf("=");
|
|
30258
|
+
if (eq !== -1) return { value: arg.slice(eq + 1), next: i };
|
|
30259
|
+
return { value: argv[i + 1], next: i + 1 };
|
|
30260
|
+
}
|
|
30261
|
+
function parseLoginArgs(argv) {
|
|
30262
|
+
const parsed = { name: DEFAULT_PROFILE_NAME, json: false, help: false };
|
|
30263
|
+
for (let i = 0; i < argv.length; i++) {
|
|
30264
|
+
const arg = argv[i];
|
|
30265
|
+
if (arg === "-h" || arg === "--help") parsed.help = true;
|
|
30266
|
+
else if (arg === "--json") parsed.json = true;
|
|
30267
|
+
else if (arg === "--endpoint" || arg.startsWith("--endpoint=")) {
|
|
30268
|
+
const { value, next } = readFlagValue(argv, i);
|
|
30269
|
+
parsed.endpoint = value;
|
|
30270
|
+
i = next;
|
|
30271
|
+
} else if (arg === "--token" || arg.startsWith("--token=")) {
|
|
30272
|
+
const { value, next } = readFlagValue(argv, i);
|
|
30273
|
+
parsed.token = value;
|
|
30274
|
+
i = next;
|
|
30275
|
+
} else if (arg === "--name" || arg.startsWith("--name=")) {
|
|
30276
|
+
const { value, next } = readFlagValue(argv, i);
|
|
30277
|
+
if (value && value.length > 0) parsed.name = value;
|
|
30278
|
+
i = next;
|
|
30279
|
+
} else {
|
|
30280
|
+
parsed.error = `unknown argument "${arg}"`;
|
|
30281
|
+
break;
|
|
30282
|
+
}
|
|
30283
|
+
}
|
|
30284
|
+
return parsed;
|
|
30285
|
+
}
|
|
30286
|
+
function printLoginHelp(out) {
|
|
30287
|
+
out("usage: neat login [--endpoint <url>] [--token <token>] [--name <name>] [--json]");
|
|
30288
|
+
out(" Connect this machine to a hosted NEAT and make it the default for the");
|
|
30289
|
+
out(" neat CLI and the MCP server. Omit --endpoint / --token to be prompted");
|
|
30290
|
+
out(" (the token is read without echo). --name labels the profile (default");
|
|
30291
|
+
out(' "hosted"). The token can also come from NEAT_LOGIN_TOKEN.');
|
|
30292
|
+
out(" Exit 0 on success, 1 rejected token/endpoint, 2 misuse, 3 unreachable.");
|
|
30293
|
+
}
|
|
30294
|
+
async function probeDaemon(fetchImpl, endpoint2, token) {
|
|
30295
|
+
const root = endpoint2.replace(/\/$/, "");
|
|
30296
|
+
let res;
|
|
30297
|
+
try {
|
|
30298
|
+
res = await fetchImpl(`${root}/health`, {
|
|
30299
|
+
headers: { authorization: `Bearer ${token}` },
|
|
30300
|
+
signal: AbortSignal.timeout(HEALTH_TIMEOUT_MS2)
|
|
30301
|
+
});
|
|
30302
|
+
} catch (err) {
|
|
30303
|
+
return { kind: "unreachable", detail: err.message };
|
|
30304
|
+
}
|
|
30305
|
+
if (res.status === 401 || res.status === 403) return { kind: "unauthorized", status: res.status };
|
|
30306
|
+
if (!res.ok) return { kind: "not-neat", status: res.status };
|
|
30307
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
30308
|
+
if (!contentType.includes("json")) return { kind: "not-neat", status: res.status };
|
|
30309
|
+
return { kind: "ok" };
|
|
30310
|
+
}
|
|
30311
|
+
async function runLoginCommand(argv, deps = {}) {
|
|
30312
|
+
const out = deps.out ?? ((line) => console.log(line));
|
|
30313
|
+
const err = deps.err ?? ((line) => console.error(line));
|
|
30314
|
+
const env = deps.env ?? process.env;
|
|
30315
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
30316
|
+
const readLine = deps.readLine ?? defaultReadLine;
|
|
30317
|
+
const readSecret = deps.readSecret ?? defaultReadSecret;
|
|
30318
|
+
const args = parseLoginArgs(argv);
|
|
30319
|
+
if (args.help) {
|
|
30320
|
+
printLoginHelp(out);
|
|
30321
|
+
return 0;
|
|
30322
|
+
}
|
|
30323
|
+
if (args.error) {
|
|
30324
|
+
err(`neat login: ${args.error}`);
|
|
30325
|
+
return 2;
|
|
30326
|
+
}
|
|
30327
|
+
let endpoint2 = args.endpoint;
|
|
30328
|
+
if (!endpoint2) endpoint2 = (await readLine("Hosted NEAT endpoint (https://\u2026): "))?.trim();
|
|
30329
|
+
if (!endpoint2) {
|
|
30330
|
+
err("neat login: an endpoint is required \u2014 pass --endpoint <url> or run interactively");
|
|
30331
|
+
return 2;
|
|
30332
|
+
}
|
|
30333
|
+
let url;
|
|
30334
|
+
try {
|
|
30335
|
+
url = new URL(endpoint2);
|
|
30336
|
+
} catch {
|
|
30337
|
+
err(`neat login: --endpoint must be an absolute URL (got "${endpoint2}")`);
|
|
30338
|
+
return 2;
|
|
30339
|
+
}
|
|
30340
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
30341
|
+
err(`neat login: --endpoint must be an http(s) URL (got "${url.protocol}")`);
|
|
30342
|
+
return 2;
|
|
30343
|
+
}
|
|
30344
|
+
const envToken = env.NEAT_LOGIN_TOKEN;
|
|
30345
|
+
let token = args.token ?? (envToken && envToken.length > 0 ? envToken : void 0);
|
|
30346
|
+
if (!token) token = (await readSecret("Daemon token: "))?.trim();
|
|
30347
|
+
if (!token) {
|
|
30348
|
+
err("neat login: a token is required \u2014 pass --token, set NEAT_LOGIN_TOKEN, or run interactively");
|
|
30349
|
+
return 2;
|
|
30350
|
+
}
|
|
30351
|
+
const probe = await probeDaemon(fetchImpl, endpoint2, token);
|
|
30352
|
+
if (probe.kind === "unreachable") {
|
|
30353
|
+
err(`neat login: can't reach ${endpoint2} \u2014 ${probe.detail}`);
|
|
30354
|
+
return 3;
|
|
30355
|
+
}
|
|
30356
|
+
if (probe.kind === "unauthorized") {
|
|
30357
|
+
err(`neat login: ${endpoint2} rejected the token (HTTP ${probe.status}). Check the token and try again.`);
|
|
30358
|
+
return 1;
|
|
30359
|
+
}
|
|
30360
|
+
if (probe.kind === "not-neat") {
|
|
30361
|
+
err(`neat login: ${endpoint2} answered but does not look like a NEAT daemon (HTTP ${probe.status}).`);
|
|
30362
|
+
return 1;
|
|
30363
|
+
}
|
|
30364
|
+
await upsertProfile(
|
|
30365
|
+
{ name: args.name, endpoint: endpoint2, authToken: token },
|
|
30366
|
+
{ makeActive: true, ...deps.home ? { home: deps.home } : {} }
|
|
30367
|
+
);
|
|
30368
|
+
if (args.json) {
|
|
30369
|
+
out(JSON.stringify({ status: "logged-in", profile: args.name, endpoint: endpoint2 }, null, 2));
|
|
30370
|
+
} else {
|
|
30371
|
+
out(`Logged in \u2014 profile "${args.name}" \u2192 ${endpoint2}`);
|
|
30372
|
+
out("The neat CLI and the MCP server now read this hosted graph by default.");
|
|
30373
|
+
out("Run `neat logout` to switch back to your local daemon.");
|
|
30374
|
+
}
|
|
30375
|
+
return 0;
|
|
30376
|
+
}
|
|
30377
|
+
function parseLogoutArgs(argv) {
|
|
30378
|
+
const parsed = { help: false };
|
|
30379
|
+
for (let i = 0; i < argv.length; i++) {
|
|
30380
|
+
const arg = argv[i];
|
|
30381
|
+
if (arg === "-h" || arg === "--help") parsed.help = true;
|
|
30382
|
+
else if (arg === "--name" || arg.startsWith("--name=")) {
|
|
30383
|
+
const { value, next } = readFlagValue(argv, i);
|
|
30384
|
+
parsed.name = value;
|
|
30385
|
+
i = next;
|
|
30386
|
+
} else {
|
|
30387
|
+
parsed.error = `unknown argument "${arg}"`;
|
|
30388
|
+
break;
|
|
30389
|
+
}
|
|
30390
|
+
}
|
|
30391
|
+
return parsed;
|
|
30392
|
+
}
|
|
30393
|
+
async function runLogoutCommand(argv, deps = {}) {
|
|
30394
|
+
const out = deps.out ?? ((line) => console.log(line));
|
|
30395
|
+
const err = deps.err ?? ((line) => console.error(line));
|
|
30396
|
+
const home = deps.home;
|
|
30397
|
+
const args = parseLogoutArgs(argv);
|
|
30398
|
+
if (args.help) {
|
|
30399
|
+
out("usage: neat logout [--name <name>]");
|
|
30400
|
+
out(" With no argument, clears the active hosted profile so the CLI and MCP");
|
|
30401
|
+
out(" server go back to your local daemon (the stored profile is kept).");
|
|
30402
|
+
out(" --name <name> removes that profile from ~/.neat/profiles.json entirely.");
|
|
30403
|
+
return 0;
|
|
30404
|
+
}
|
|
30405
|
+
if (args.error) {
|
|
30406
|
+
err(`neat logout: ${args.error}`);
|
|
30407
|
+
return 2;
|
|
30408
|
+
}
|
|
30409
|
+
if (args.name !== void 0) {
|
|
30410
|
+
if (args.name.length === 0) {
|
|
30411
|
+
err("neat logout: --name needs a profile name");
|
|
30412
|
+
return 2;
|
|
30413
|
+
}
|
|
30414
|
+
const removed = await removeProfile(args.name, home ?? void 0);
|
|
30415
|
+
if (!removed) {
|
|
30416
|
+
err(`neat logout: no profile named "${args.name}"`);
|
|
30417
|
+
return 1;
|
|
30418
|
+
}
|
|
30419
|
+
out(`Removed profile "${args.name}".`);
|
|
30420
|
+
return 0;
|
|
30421
|
+
}
|
|
30422
|
+
const active = await getActiveProfile(home ?? void 0);
|
|
30423
|
+
if (!active) {
|
|
30424
|
+
out("Not logged in to a hosted NEAT \u2014 the CLI is already using your local daemon.");
|
|
30425
|
+
return 0;
|
|
30426
|
+
}
|
|
30427
|
+
await clearActiveProfile(home ?? void 0);
|
|
30428
|
+
out(`Logged out of "${active.name}" (${active.endpoint}). The CLI is back on your local daemon.`);
|
|
30429
|
+
return 0;
|
|
30430
|
+
}
|
|
30431
|
+
async function defaultReadLine(prompt) {
|
|
30432
|
+
if (!process.stdin.isTTY) return void 0;
|
|
30433
|
+
const rl = import_promises2.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
30434
|
+
try {
|
|
30435
|
+
return await rl.question(prompt);
|
|
30436
|
+
} finally {
|
|
30437
|
+
rl.close();
|
|
30438
|
+
}
|
|
30439
|
+
}
|
|
30440
|
+
async function defaultReadSecret(prompt) {
|
|
30441
|
+
const stdin = process.stdin;
|
|
30442
|
+
if (!stdin.isTTY) return void 0;
|
|
30443
|
+
process.stdout.write(prompt);
|
|
30444
|
+
return new Promise((resolve) => {
|
|
30445
|
+
const chars = [];
|
|
30446
|
+
stdin.setRawMode(true);
|
|
30447
|
+
stdin.resume();
|
|
30448
|
+
stdin.setEncoding("utf8");
|
|
30449
|
+
const cleanup = () => {
|
|
30450
|
+
stdin.setRawMode(false);
|
|
30451
|
+
stdin.pause();
|
|
30452
|
+
stdin.off("data", onData);
|
|
30453
|
+
};
|
|
30454
|
+
const onData = (ch) => {
|
|
30455
|
+
const code = ch.charCodeAt(0);
|
|
30456
|
+
if (ch === "\n" || ch === "\r" || code === 4) {
|
|
30457
|
+
cleanup();
|
|
30458
|
+
process.stdout.write("\n");
|
|
30459
|
+
resolve(chars.join(""));
|
|
30460
|
+
} else if (code === 3) {
|
|
30461
|
+
cleanup();
|
|
30462
|
+
process.stdout.write("\n");
|
|
30463
|
+
process.exit(130);
|
|
30464
|
+
} else if (code === 127 || ch === "\b") {
|
|
30465
|
+
chars.pop();
|
|
30466
|
+
} else {
|
|
30467
|
+
chars.push(ch);
|
|
30468
|
+
}
|
|
30469
|
+
};
|
|
30470
|
+
stdin.on("data", onData);
|
|
30471
|
+
});
|
|
30472
|
+
}
|
|
30473
|
+
|
|
30474
|
+
// src/hooks-cli.ts
|
|
30475
|
+
init_cjs_shims();
|
|
30476
|
+
var import_node_path91 = __toESM(require("path"), 1);
|
|
30477
|
+
var import_node_os7 = __toESM(require("os"), 1);
|
|
30478
|
+
var import_node_fs55 = require("fs");
|
|
30041
30479
|
var import_node_url5 = require("url");
|
|
30042
30480
|
var HOOK_FILENAME = "neat-search-nudge.mjs";
|
|
30043
30481
|
var GUIDE_FILENAME = "GRAPH_FIRST.md";
|
|
30044
30482
|
var GUIDE_INSTALL_NAME = "neat-graph-first.md";
|
|
30045
30483
|
var HOOK_MATCHER = "Grep|Glob|Bash";
|
|
30046
30484
|
function moduleDir() {
|
|
30047
|
-
return typeof __dirname !== "undefined" ? __dirname :
|
|
30485
|
+
return typeof __dirname !== "undefined" ? __dirname : import_node_path91.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
|
|
30048
30486
|
}
|
|
30049
30487
|
async function readSkillAsset(rel) {
|
|
30050
30488
|
const here = moduleDir();
|
|
30051
30489
|
const candidates = [
|
|
30052
|
-
|
|
30053
|
-
|
|
30054
|
-
|
|
30490
|
+
import_node_path91.default.resolve(here, "../../claude-skill", rel),
|
|
30491
|
+
import_node_path91.default.resolve(here, "../../../claude-skill", rel),
|
|
30492
|
+
import_node_path91.default.resolve(here, "../claude-skill", rel)
|
|
30055
30493
|
];
|
|
30056
30494
|
for (const candidate of candidates) {
|
|
30057
30495
|
try {
|
|
30058
|
-
return await
|
|
30496
|
+
return await import_node_fs55.promises.readFile(candidate, "utf8");
|
|
30059
30497
|
} catch {
|
|
30060
30498
|
}
|
|
30061
30499
|
}
|
|
@@ -30063,22 +30501,22 @@ async function readSkillAsset(rel) {
|
|
|
30063
30501
|
`neat hooks: could not find @neat.is/claude-skill/${rel} \u2014 is the package installed?`
|
|
30064
30502
|
);
|
|
30065
30503
|
}
|
|
30066
|
-
function
|
|
30504
|
+
function neatHome4() {
|
|
30067
30505
|
const override = process.env.NEAT_HOME;
|
|
30068
|
-
if (override && override.length > 0) return
|
|
30069
|
-
return
|
|
30506
|
+
if (override && override.length > 0) return import_node_path91.default.resolve(override);
|
|
30507
|
+
return import_node_path91.default.join(import_node_os7.default.homedir(), ".neat");
|
|
30070
30508
|
}
|
|
30071
30509
|
function claudeSettingsPath() {
|
|
30072
30510
|
const override = process.env.NEAT_CLAUDE_SETTINGS;
|
|
30073
|
-
if (override && override.length > 0) return
|
|
30074
|
-
const home = process.env.HOME ?? process.env.USERPROFILE ??
|
|
30075
|
-
return
|
|
30511
|
+
if (override && override.length > 0) return import_node_path91.default.resolve(override);
|
|
30512
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os7.default.homedir();
|
|
30513
|
+
return import_node_path91.default.join(home, ".claude", "settings.json");
|
|
30076
30514
|
}
|
|
30077
30515
|
function installedHookPath() {
|
|
30078
|
-
return
|
|
30516
|
+
return import_node_path91.default.join(neatHome4(), "hooks", HOOK_FILENAME);
|
|
30079
30517
|
}
|
|
30080
30518
|
function gateFlagPath() {
|
|
30081
|
-
return
|
|
30519
|
+
return import_node_path91.default.join(neatHome4(), "hooks", "gate-enabled");
|
|
30082
30520
|
}
|
|
30083
30521
|
function isNeatSearchEntry(entry2) {
|
|
30084
30522
|
return (entry2.hooks ?? []).some(
|
|
@@ -30111,14 +30549,14 @@ async function runHooks(opts) {
|
|
|
30111
30549
|
const hookScript = await readSkillAsset(`hooks/${HOOK_FILENAME}`);
|
|
30112
30550
|
const guide = await readSkillAsset(GUIDE_FILENAME);
|
|
30113
30551
|
const scriptPath = installedHookPath();
|
|
30114
|
-
await
|
|
30115
|
-
await
|
|
30116
|
-
const guidePath =
|
|
30117
|
-
await
|
|
30552
|
+
await import_node_fs55.promises.mkdir(import_node_path91.default.dirname(scriptPath), { recursive: true });
|
|
30553
|
+
await import_node_fs55.promises.writeFile(scriptPath, hookScript, { mode: 493 });
|
|
30554
|
+
const guidePath = import_node_path91.default.join(neatHome4(), GUIDE_INSTALL_NAME);
|
|
30555
|
+
await import_node_fs55.promises.writeFile(guidePath, guide, "utf8");
|
|
30118
30556
|
const settingsFile = claudeSettingsPath();
|
|
30119
30557
|
let settings = {};
|
|
30120
30558
|
try {
|
|
30121
|
-
settings = JSON.parse(await
|
|
30559
|
+
settings = JSON.parse(await import_node_fs55.promises.readFile(settingsFile, "utf8"));
|
|
30122
30560
|
} catch (err) {
|
|
30123
30561
|
if (err.code !== "ENOENT") {
|
|
30124
30562
|
console.error(
|
|
@@ -30140,14 +30578,14 @@ async function runHooks(opts) {
|
|
|
30140
30578
|
...settings,
|
|
30141
30579
|
hooks: { ...hooks, PreToolUse: preToolUse }
|
|
30142
30580
|
};
|
|
30143
|
-
await
|
|
30144
|
-
await
|
|
30581
|
+
await import_node_fs55.promises.mkdir(import_node_path91.default.dirname(settingsFile), { recursive: true });
|
|
30582
|
+
await import_node_fs55.promises.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
30145
30583
|
const flag = gateFlagPath();
|
|
30146
30584
|
if (opts.gate) {
|
|
30147
|
-
await
|
|
30148
|
-
await
|
|
30585
|
+
await import_node_fs55.promises.mkdir(import_node_path91.default.dirname(flag), { recursive: true });
|
|
30586
|
+
await import_node_fs55.promises.writeFile(flag, "1\n", "utf8");
|
|
30149
30587
|
} else {
|
|
30150
|
-
await
|
|
30588
|
+
await import_node_fs55.promises.rm(flag, { force: true });
|
|
30151
30589
|
}
|
|
30152
30590
|
const mode = opts.gate ? "GATE (deny search until you ask the graph)" : "nudge (search still runs)";
|
|
30153
30591
|
console.log(`neat hooks: installed the search hook in ${opts.gate ? "gate" : "nudge"} mode`);
|
|
@@ -30237,8 +30675,8 @@ async function runHooksCommand(args) {
|
|
|
30237
30675
|
|
|
30238
30676
|
// src/claude-cli.ts
|
|
30239
30677
|
init_cjs_shims();
|
|
30240
|
-
var
|
|
30241
|
-
var
|
|
30678
|
+
var import_node_path92 = __toESM(require("path"), 1);
|
|
30679
|
+
var import_node_fs56 = require("fs");
|
|
30242
30680
|
var NEAT_SECTION_HEADING = "## neat";
|
|
30243
30681
|
var NEAT_DIRECTIVE_BODY = `This project has NEAT wired in: a live, fused semantic graph of the system \u2014
|
|
30244
30682
|
code and runtime behaviour (OpenTelemetry) in one model, every fact tagged with
|
|
@@ -30272,8 +30710,8 @@ ${NEAT_DIRECTIVE_BODY}
|
|
|
30272
30710
|
}
|
|
30273
30711
|
function claudeMdPath() {
|
|
30274
30712
|
const override = process.env.NEAT_CLAUDE_MD;
|
|
30275
|
-
if (override && override.length > 0) return
|
|
30276
|
-
return
|
|
30713
|
+
if (override && override.length > 0) return import_node_path92.default.resolve(override);
|
|
30714
|
+
return import_node_path92.default.join(process.cwd(), "CLAUDE.md");
|
|
30277
30715
|
}
|
|
30278
30716
|
function splitAroundSection(raw) {
|
|
30279
30717
|
const lines = raw.split("\n");
|
|
@@ -30301,7 +30739,7 @@ function compose(before, after) {
|
|
|
30301
30739
|
}
|
|
30302
30740
|
async function readIfExists2(file) {
|
|
30303
30741
|
try {
|
|
30304
|
-
return await
|
|
30742
|
+
return await import_node_fs56.promises.readFile(file, "utf8");
|
|
30305
30743
|
} catch (err) {
|
|
30306
30744
|
if (err.code === "ENOENT") return null;
|
|
30307
30745
|
throw err;
|
|
@@ -30312,8 +30750,8 @@ async function runInstall() {
|
|
|
30312
30750
|
const raw = await readIfExists2(file) ?? "";
|
|
30313
30751
|
const { before, after, found } = splitAroundSection(raw);
|
|
30314
30752
|
const next = compose(before, after);
|
|
30315
|
-
await
|
|
30316
|
-
await
|
|
30753
|
+
await import_node_fs56.promises.mkdir(import_node_path92.default.dirname(file), { recursive: true });
|
|
30754
|
+
await import_node_fs56.promises.writeFile(file, next, "utf8");
|
|
30317
30755
|
const verb = raw.length === 0 ? "created" : found ? "refreshed" : "added";
|
|
30318
30756
|
console.log(`neat claude: ${verb} the \`${NEAT_SECTION_HEADING}\` section in ${file}`);
|
|
30319
30757
|
console.log("Your agent will now reach for `neat ask` before Read/Grep/Bash. Restart the");
|
|
@@ -30334,7 +30772,7 @@ async function runUninstall() {
|
|
|
30334
30772
|
}
|
|
30335
30773
|
const remaining = [before, after].filter((s) => s.length > 0).join("\n\n");
|
|
30336
30774
|
const next = remaining.length > 0 ? remaining.replace(/\n*$/, "") + "\n" : "";
|
|
30337
|
-
await
|
|
30775
|
+
await import_node_fs56.promises.writeFile(file, next, "utf8");
|
|
30338
30776
|
console.log(`neat claude: removed the \`${NEAT_SECTION_HEADING}\` section from ${file}.`);
|
|
30339
30777
|
return { exitCode: 0 };
|
|
30340
30778
|
}
|
|
@@ -30377,9 +30815,9 @@ async function runClaudeCommand(args) {
|
|
|
30377
30815
|
|
|
30378
30816
|
// src/codex-cli.ts
|
|
30379
30817
|
init_cjs_shims();
|
|
30380
|
-
var
|
|
30381
|
-
var
|
|
30382
|
-
var
|
|
30818
|
+
var import_node_path93 = __toESM(require("path"), 1);
|
|
30819
|
+
var import_node_os8 = __toESM(require("os"), 1);
|
|
30820
|
+
var import_node_fs57 = require("fs");
|
|
30383
30821
|
var import_node_util = require("util");
|
|
30384
30822
|
var import_smol_toml6 = require("smol-toml");
|
|
30385
30823
|
var CODEX_MCP_SERVER = {
|
|
@@ -30397,14 +30835,14 @@ var NEAT_GRAPH_FIRST_START = "<!-- neat:graph-first -->";
|
|
|
30397
30835
|
var NEAT_GRAPH_FIRST_END = "<!-- /neat:graph-first -->";
|
|
30398
30836
|
function codexConfigPath() {
|
|
30399
30837
|
const override = process.env.NEAT_CODEX_CONFIG;
|
|
30400
|
-
if (override && override.length > 0) return
|
|
30401
|
-
const home = process.env.HOME ?? process.env.USERPROFILE ??
|
|
30402
|
-
return
|
|
30838
|
+
if (override && override.length > 0) return import_node_path93.default.resolve(override);
|
|
30839
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os8.default.homedir();
|
|
30840
|
+
return import_node_path93.default.join(home, ".codex", "config.toml");
|
|
30403
30841
|
}
|
|
30404
30842
|
function agentsFilePath() {
|
|
30405
30843
|
const override = process.env.NEAT_CODEX_AGENTS;
|
|
30406
|
-
if (override && override.length > 0) return
|
|
30407
|
-
return
|
|
30844
|
+
if (override && override.length > 0) return import_node_path93.default.resolve(override);
|
|
30845
|
+
return import_node_path93.default.join(process.cwd(), "AGENTS.md");
|
|
30408
30846
|
}
|
|
30409
30847
|
function isTableHeader(line) {
|
|
30410
30848
|
return /^\s*\[\[?[^\]]+\]\]?\s*$/.test(line);
|
|
@@ -30538,7 +30976,7 @@ async function runCodex(opts) {
|
|
|
30538
30976
|
const agentsPath = agentsFilePath();
|
|
30539
30977
|
let configRaw = "";
|
|
30540
30978
|
try {
|
|
30541
|
-
configRaw = await
|
|
30979
|
+
configRaw = await import_node_fs57.promises.readFile(configPath, "utf8");
|
|
30542
30980
|
} catch (err) {
|
|
30543
30981
|
if (err.code !== "ENOENT") {
|
|
30544
30982
|
console.error(`neat codex: failed to read ${configPath} \u2014 ${err.message}`);
|
|
@@ -30547,7 +30985,7 @@ async function runCodex(opts) {
|
|
|
30547
30985
|
}
|
|
30548
30986
|
let agentsRaw = "";
|
|
30549
30987
|
try {
|
|
30550
|
-
agentsRaw = await
|
|
30988
|
+
agentsRaw = await import_node_fs57.promises.readFile(agentsPath, "utf8");
|
|
30551
30989
|
} catch (err) {
|
|
30552
30990
|
if (err.code !== "ENOENT") {
|
|
30553
30991
|
console.error(`neat codex: failed to read ${agentsPath} \u2014 ${err.message}`);
|
|
@@ -30587,15 +31025,15 @@ async function runCodex(opts) {
|
|
|
30587
31025
|
return { exitCode: 0 };
|
|
30588
31026
|
}
|
|
30589
31027
|
if (config.changed) {
|
|
30590
|
-
await
|
|
30591
|
-
await
|
|
31028
|
+
await import_node_fs57.promises.mkdir(import_node_path93.default.dirname(configPath), { recursive: true });
|
|
31029
|
+
await import_node_fs57.promises.writeFile(configPath, config.text, "utf8");
|
|
30592
31030
|
console.log(`neat codex: wrote [mcp_servers.neat] to ${configPath}`);
|
|
30593
31031
|
} else {
|
|
30594
31032
|
console.log(`neat codex: ${configPath} already has NEAT's MCP server`);
|
|
30595
31033
|
}
|
|
30596
31034
|
if (agents.changed) {
|
|
30597
|
-
await
|
|
30598
|
-
await
|
|
31035
|
+
await import_node_fs57.promises.mkdir(import_node_path93.default.dirname(agentsPath), { recursive: true });
|
|
31036
|
+
await import_node_fs57.promises.writeFile(agentsPath, agents.text, "utf8");
|
|
30599
31037
|
console.log(`neat codex: wrote the graph-first block to ${agentsPath}`);
|
|
30600
31038
|
} else {
|
|
30601
31039
|
console.log(`neat codex: ${agentsPath} already has the graph-first block`);
|
|
@@ -30652,9 +31090,9 @@ async function runCodexCommand(args) {
|
|
|
30652
31090
|
|
|
30653
31091
|
// src/editors-cli.ts
|
|
30654
31092
|
init_cjs_shims();
|
|
30655
|
-
var
|
|
30656
|
-
var
|
|
30657
|
-
var
|
|
31093
|
+
var import_node_path94 = __toESM(require("path"), 1);
|
|
31094
|
+
var import_node_os9 = __toESM(require("os"), 1);
|
|
31095
|
+
var import_node_fs58 = require("fs");
|
|
30658
31096
|
var import_node_util2 = require("util");
|
|
30659
31097
|
var jsonc = __toESM(require("jsonc-parser"), 1);
|
|
30660
31098
|
var NEAT_MCP_SERVER = {
|
|
@@ -30674,21 +31112,21 @@ var NEAT_CRUSH_SERVER = {
|
|
|
30674
31112
|
var GRAPH_FIRST_MARKER_OPEN = "<!-- neat:graph-first -->";
|
|
30675
31113
|
var GRAPH_FIRST_MARKER_CLOSE = "<!-- /neat:graph-first -->";
|
|
30676
31114
|
function homeDir() {
|
|
30677
|
-
return process.env.HOME ?? process.env.USERPROFILE ??
|
|
31115
|
+
return process.env.HOME ?? process.env.USERPROFILE ?? import_node_os9.default.homedir();
|
|
30678
31116
|
}
|
|
30679
31117
|
function xdgConfigDir() {
|
|
30680
31118
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
30681
|
-
return xdg && xdg.length > 0 ?
|
|
31119
|
+
return xdg && xdg.length > 0 ? import_node_path94.default.resolve(xdg) : import_node_path94.default.join(homeDir(), ".config");
|
|
30682
31120
|
}
|
|
30683
31121
|
function envOverride(name) {
|
|
30684
31122
|
const v = process.env[name];
|
|
30685
|
-
return v && v.length > 0 ?
|
|
31123
|
+
return v && v.length > 0 ? import_node_path94.default.resolve(v) : void 0;
|
|
30686
31124
|
}
|
|
30687
31125
|
var CURSOR_CLIENT = {
|
|
30688
31126
|
id: "cursor",
|
|
30689
31127
|
label: "Cursor",
|
|
30690
31128
|
docsUrl: "https://docs.cursor.com/context/mcp",
|
|
30691
|
-
mcpConfigPath: () => envOverride("NEAT_CURSOR_CONFIG") ??
|
|
31129
|
+
mcpConfigPath: () => envOverride("NEAT_CURSOR_CONFIG") ?? import_node_path94.default.join(homeDir(), ".cursor", "mcp.json"),
|
|
30692
31130
|
mcpContainerKey: "mcpServers",
|
|
30693
31131
|
format: "json",
|
|
30694
31132
|
// Cursor still reads a single `.cursorrules` at the project root (the modern
|
|
@@ -30700,7 +31138,7 @@ var DEVIN_CLIENT = {
|
|
|
30700
31138
|
id: "devin",
|
|
30701
31139
|
label: "Devin Desktop (Cascade)",
|
|
30702
31140
|
docsUrl: "https://docs.devin.ai/desktop/cascade/mcp",
|
|
30703
|
-
mcpConfigPath: () => envOverride("NEAT_DEVIN_CONFIG") ??
|
|
31141
|
+
mcpConfigPath: () => envOverride("NEAT_DEVIN_CONFIG") ?? import_node_path94.default.join(homeDir(), ".codeium", "windsurf", "mcp_config.json"),
|
|
30704
31142
|
mcpContainerKey: "mcpServers",
|
|
30705
31143
|
format: "json",
|
|
30706
31144
|
rulesFileName: ".windsurfrules"
|
|
@@ -30709,7 +31147,7 @@ var GEMINI_CLIENT = {
|
|
|
30709
31147
|
id: "gemini",
|
|
30710
31148
|
label: "Gemini CLI",
|
|
30711
31149
|
docsUrl: "https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md",
|
|
30712
|
-
mcpConfigPath: () => envOverride("NEAT_GEMINI_CONFIG") ??
|
|
31150
|
+
mcpConfigPath: () => envOverride("NEAT_GEMINI_CONFIG") ?? import_node_path94.default.join(homeDir(), ".gemini", "settings.json"),
|
|
30713
31151
|
mcpContainerKey: "mcpServers",
|
|
30714
31152
|
format: "json",
|
|
30715
31153
|
rulesFileName: "GEMINI.md"
|
|
@@ -30718,7 +31156,7 @@ var QWEN_CLIENT = {
|
|
|
30718
31156
|
id: "qwen",
|
|
30719
31157
|
label: "Qwen Code",
|
|
30720
31158
|
docsUrl: "https://qwenlm.github.io/qwen-code-docs/en/users/features/mcp/",
|
|
30721
|
-
mcpConfigPath: () => envOverride("NEAT_QWEN_CONFIG") ??
|
|
31159
|
+
mcpConfigPath: () => envOverride("NEAT_QWEN_CONFIG") ?? import_node_path94.default.join(homeDir(), ".qwen", "settings.json"),
|
|
30722
31160
|
mcpContainerKey: "mcpServers",
|
|
30723
31161
|
format: "json",
|
|
30724
31162
|
rulesFileName: "QWEN.md"
|
|
@@ -30727,7 +31165,7 @@ var AMAZONQ_CLIENT = {
|
|
|
30727
31165
|
id: "amazonq",
|
|
30728
31166
|
label: "Amazon Q Developer CLI",
|
|
30729
31167
|
docsUrl: "https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-mcp-configuration.html",
|
|
30730
|
-
mcpConfigPath: () => envOverride("NEAT_AMAZONQ_CONFIG") ??
|
|
31168
|
+
mcpConfigPath: () => envOverride("NEAT_AMAZONQ_CONFIG") ?? import_node_path94.default.join(homeDir(), ".aws", "amazonq", "mcp.json"),
|
|
30731
31169
|
mcpContainerKey: "mcpServers",
|
|
30732
31170
|
format: "json"
|
|
30733
31171
|
};
|
|
@@ -30735,7 +31173,7 @@ var ROOCODE_CLIENT = {
|
|
|
30735
31173
|
id: "roocode",
|
|
30736
31174
|
label: "Roo Code",
|
|
30737
31175
|
docsUrl: "https://roocodeinc.github.io/Roo-Code/features/mcp/using-mcp-in-roo",
|
|
30738
|
-
mcpConfigPath: () => envOverride("NEAT_ROOCODE_CONFIG") ??
|
|
31176
|
+
mcpConfigPath: () => envOverride("NEAT_ROOCODE_CONFIG") ?? import_node_path94.default.join(process.cwd(), ".roo", "mcp.json"),
|
|
30739
31177
|
mcpContainerKey: "mcpServers",
|
|
30740
31178
|
format: "json"
|
|
30741
31179
|
};
|
|
@@ -30748,9 +31186,9 @@ var ZED_CLIENT = {
|
|
|
30748
31186
|
if (override) return override;
|
|
30749
31187
|
if (process.platform === "win32") {
|
|
30750
31188
|
const appData = process.env.APPDATA;
|
|
30751
|
-
if (appData && appData.length > 0) return
|
|
31189
|
+
if (appData && appData.length > 0) return import_node_path94.default.join(appData, "Zed", "settings.json");
|
|
30752
31190
|
}
|
|
30753
|
-
return
|
|
31191
|
+
return import_node_path94.default.join(homeDir(), ".config", "zed", "settings.json");
|
|
30754
31192
|
},
|
|
30755
31193
|
mcpContainerKey: "context_servers",
|
|
30756
31194
|
format: "jsonc",
|
|
@@ -30760,7 +31198,7 @@ var OPENCODE_CLIENT = {
|
|
|
30760
31198
|
id: "opencode",
|
|
30761
31199
|
label: "OpenCode",
|
|
30762
31200
|
docsUrl: "https://opencode.ai/docs/mcp-servers/",
|
|
30763
|
-
mcpConfigPath: () => envOverride("NEAT_OPENCODE_CONFIG") ??
|
|
31201
|
+
mcpConfigPath: () => envOverride("NEAT_OPENCODE_CONFIG") ?? import_node_path94.default.join(xdgConfigDir(), "opencode", "opencode.json"),
|
|
30764
31202
|
mcpContainerKey: "mcp",
|
|
30765
31203
|
format: "json",
|
|
30766
31204
|
serverEntry: NEAT_OPENCODE_SERVER,
|
|
@@ -30770,7 +31208,7 @@ var CRUSH_CLIENT = {
|
|
|
30770
31208
|
id: "crush",
|
|
30771
31209
|
label: "Crush",
|
|
30772
31210
|
docsUrl: "https://charmbracelet-crush.mintlify.app/configuration/mcp",
|
|
30773
|
-
mcpConfigPath: () => envOverride("NEAT_CRUSH_CONFIG") ??
|
|
31211
|
+
mcpConfigPath: () => envOverride("NEAT_CRUSH_CONFIG") ?? import_node_path94.default.join(xdgConfigDir(), "crush", "crush.json"),
|
|
30774
31212
|
mcpContainerKey: "mcp",
|
|
30775
31213
|
format: "json",
|
|
30776
31214
|
serverEntry: NEAT_CRUSH_SERVER,
|
|
@@ -30833,7 +31271,7 @@ async function planMcp(client, mcpPath) {
|
|
|
30833
31271
|
const serverEntry = client.serverEntry ?? NEAT_MCP_SERVER;
|
|
30834
31272
|
let raw = "";
|
|
30835
31273
|
try {
|
|
30836
|
-
raw = await
|
|
31274
|
+
raw = await import_node_fs58.promises.readFile(mcpPath, "utf8");
|
|
30837
31275
|
} catch (err) {
|
|
30838
31276
|
const e = err;
|
|
30839
31277
|
if (e.code === "ENOENT") {
|
|
@@ -30875,7 +31313,7 @@ async function runEditorInstall(client, opts) {
|
|
|
30875
31313
|
const mcpPath = client.mcpConfigPath();
|
|
30876
31314
|
const serverEntry = client.serverEntry ?? NEAT_MCP_SERVER;
|
|
30877
31315
|
const hasRules = typeof client.rulesFileName === "string";
|
|
30878
|
-
const rulesPath = hasRules ?
|
|
31316
|
+
const rulesPath = hasRules ? import_node_path94.default.join(opts.projectDir, client.rulesFileName) : "";
|
|
30879
31317
|
const mcp = await planMcp(client, mcpPath);
|
|
30880
31318
|
if (mcp === null) return { exitCode: 1 };
|
|
30881
31319
|
let existingRules = "";
|
|
@@ -30884,7 +31322,7 @@ async function runEditorInstall(client, opts) {
|
|
|
30884
31322
|
let block = "";
|
|
30885
31323
|
if (hasRules) {
|
|
30886
31324
|
try {
|
|
30887
|
-
existingRules = await
|
|
31325
|
+
existingRules = await import_node_fs58.promises.readFile(rulesPath, "utf8");
|
|
30888
31326
|
} catch (err) {
|
|
30889
31327
|
if (err.code !== "ENOENT") {
|
|
30890
31328
|
console.error(`neat ${client.id}: failed to read ${rulesPath} \u2014 ${err.message}`);
|
|
@@ -30918,11 +31356,11 @@ async function runEditorInstall(client, opts) {
|
|
|
30918
31356
|
);
|
|
30919
31357
|
return { exitCode: 0 };
|
|
30920
31358
|
}
|
|
30921
|
-
await
|
|
30922
|
-
await
|
|
31359
|
+
await import_node_fs58.promises.mkdir(import_node_path94.default.dirname(mcpPath), { recursive: true });
|
|
31360
|
+
await import_node_fs58.promises.writeFile(mcpPath, mcp.text, "utf8");
|
|
30923
31361
|
if (hasRules) {
|
|
30924
|
-
await
|
|
30925
|
-
await
|
|
31362
|
+
await import_node_fs58.promises.mkdir(import_node_path94.default.dirname(rulesPath), { recursive: true });
|
|
31363
|
+
await import_node_fs58.promises.writeFile(rulesPath, newRules, "utf8");
|
|
30926
31364
|
}
|
|
30927
31365
|
console.log(`neat ${client.id}: wired NEAT into ${client.label}`);
|
|
30928
31366
|
console.log(` MCP server: ${mcpPath} (${client.mcpContainerKey}.neat \u2192 npx -y @neat.is/mcp)`);
|
|
@@ -31415,7 +31853,7 @@ function sleep(ms, signal) {
|
|
|
31415
31853
|
|
|
31416
31854
|
// src/cli-verbs.ts
|
|
31417
31855
|
init_cjs_shims();
|
|
31418
|
-
var
|
|
31856
|
+
var import_node_path95 = __toESM(require("path"), 1);
|
|
31419
31857
|
async function resolveProjectEntry(opts) {
|
|
31420
31858
|
const entries = await listProjects();
|
|
31421
31859
|
if (opts.project) {
|
|
@@ -31425,7 +31863,7 @@ async function resolveProjectEntry(opts) {
|
|
|
31425
31863
|
const cwd = opts.cwd ?? process.cwd();
|
|
31426
31864
|
const resolvedCwd = await normalizeProjectPath(cwd);
|
|
31427
31865
|
for (const entry2 of entries) {
|
|
31428
|
-
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${
|
|
31866
|
+
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path95.default.sep}`)) {
|
|
31429
31867
|
return entry2;
|
|
31430
31868
|
}
|
|
31431
31869
|
}
|
|
@@ -31730,6 +32168,12 @@ function usage5() {
|
|
|
31730
32168
|
console.log(" doctor Preflight this directory's setup \u2014 Node version, project,");
|
|
31731
32169
|
console.log(" and daemon reachability \u2014 and print a fix for anything down.");
|
|
31732
32170
|
console.log(" Flags: --json. Exits 0 when all pass, 1 when a check fails.");
|
|
32171
|
+
console.log(" login Connect this machine to a hosted NEAT and make it the default");
|
|
32172
|
+
console.log(" for the CLI and the MCP server. Paste the daemon endpoint +");
|
|
32173
|
+
console.log(" token, or run interactively (the token is read without echo).");
|
|
32174
|
+
console.log(" Flags: --endpoint <url>, --token <token>, --name <name>, --json.");
|
|
32175
|
+
console.log(" logout Clear the active hosted profile (back to your local daemon);");
|
|
32176
|
+
console.log(" --name <name> removes that profile entirely.");
|
|
31733
32177
|
console.log("");
|
|
31734
32178
|
console.log("query commands (mirror the MCP tools, ADR-050):");
|
|
31735
32179
|
console.log(" ask <question> Plain-language door: resolves the question to");
|
|
@@ -31780,6 +32224,7 @@ function usage5() {
|
|
|
31780
32224
|
}
|
|
31781
32225
|
var STRING_FLAGS = [
|
|
31782
32226
|
["--project", "project"],
|
|
32227
|
+
["--profile", "profile"],
|
|
31783
32228
|
["--depth", "depth"],
|
|
31784
32229
|
["--limit", "limit"],
|
|
31785
32230
|
["--edge-type", "edgeType"],
|
|
@@ -31797,6 +32242,7 @@ function parseArgs(rest) {
|
|
|
31797
32242
|
const positional = [];
|
|
31798
32243
|
const out = {
|
|
31799
32244
|
project: null,
|
|
32245
|
+
profile: null,
|
|
31800
32246
|
apply: false,
|
|
31801
32247
|
dryRun: false,
|
|
31802
32248
|
noInstall: false,
|
|
@@ -31955,7 +32401,7 @@ async function buildPatchSections(services, project) {
|
|
|
31955
32401
|
}
|
|
31956
32402
|
async function runInit(opts) {
|
|
31957
32403
|
const written = [];
|
|
31958
|
-
const stat = await
|
|
32404
|
+
const stat = await import_node_fs59.promises.stat(opts.scanPath).catch(() => null);
|
|
31959
32405
|
if (!stat || !stat.isDirectory()) {
|
|
31960
32406
|
console.error(`neat init: ${opts.scanPath} is not a directory`);
|
|
31961
32407
|
return { exitCode: 2, writtenFiles: written };
|
|
@@ -31964,13 +32410,13 @@ async function runInit(opts) {
|
|
|
31964
32410
|
printDiscoveryReport(opts, services);
|
|
31965
32411
|
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
31966
32412
|
const patch = renderPatch(sections);
|
|
31967
|
-
const patchPath =
|
|
32413
|
+
const patchPath = import_node_path96.default.join(opts.scanPath, "neat.patch");
|
|
31968
32414
|
if (opts.dryRun) {
|
|
31969
|
-
await
|
|
32415
|
+
await import_node_fs59.promises.writeFile(patchPath, patch, "utf8");
|
|
31970
32416
|
written.push(patchPath);
|
|
31971
32417
|
console.log(`dry-run: patch written to ${patchPath}`);
|
|
31972
|
-
const gitignorePath =
|
|
31973
|
-
const gitignoreExists = await
|
|
32418
|
+
const gitignorePath = import_node_path96.default.join(opts.scanPath, ".gitignore");
|
|
32419
|
+
const gitignoreExists = await import_node_fs59.promises.stat(gitignorePath).then(() => true).catch(() => false);
|
|
31974
32420
|
const verb = gitignoreExists ? "append" : "create";
|
|
31975
32421
|
console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
|
|
31976
32422
|
console.log("rerun without --dry-run to register and snapshot.");
|
|
@@ -31981,9 +32427,9 @@ async function runInit(opts) {
|
|
|
31981
32427
|
const graph = getGraph(graphKey);
|
|
31982
32428
|
const projectPaths = pathsForProject(
|
|
31983
32429
|
graphKey,
|
|
31984
|
-
|
|
32430
|
+
import_node_path96.default.join(opts.scanPath, "neat-out")
|
|
31985
32431
|
);
|
|
31986
|
-
const errorsPath =
|
|
32432
|
+
const errorsPath = import_node_path96.default.join(import_node_path96.default.dirname(opts.outPath), import_node_path96.default.basename(projectPaths.errorsPath));
|
|
31987
32433
|
const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
|
|
31988
32434
|
await saveGraphToDisk(graph, opts.outPath);
|
|
31989
32435
|
written.push(opts.outPath);
|
|
@@ -32062,7 +32508,7 @@ async function runInit(opts) {
|
|
|
32062
32508
|
console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
|
|
32063
32509
|
}
|
|
32064
32510
|
} else {
|
|
32065
|
-
await
|
|
32511
|
+
await import_node_fs59.promises.writeFile(patchPath, patch, "utf8");
|
|
32066
32512
|
written.push(patchPath);
|
|
32067
32513
|
}
|
|
32068
32514
|
}
|
|
@@ -32103,9 +32549,9 @@ var CLAUDE_SKILL_CONFIG = {
|
|
|
32103
32549
|
};
|
|
32104
32550
|
function claudeConfigPath() {
|
|
32105
32551
|
const override = process.env.NEAT_CLAUDE_CONFIG;
|
|
32106
|
-
if (override && override.length > 0) return
|
|
32552
|
+
if (override && override.length > 0) return import_node_path96.default.resolve(override);
|
|
32107
32553
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
32108
|
-
return
|
|
32554
|
+
return import_node_path96.default.join(home, ".claude.json");
|
|
32109
32555
|
}
|
|
32110
32556
|
async function runSkill(opts) {
|
|
32111
32557
|
const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
|
|
@@ -32117,7 +32563,7 @@ async function runSkill(opts) {
|
|
|
32117
32563
|
const target = claudeConfigPath();
|
|
32118
32564
|
let existing = {};
|
|
32119
32565
|
try {
|
|
32120
|
-
existing = JSON.parse(await
|
|
32566
|
+
existing = JSON.parse(await import_node_fs59.promises.readFile(target, "utf8"));
|
|
32121
32567
|
} catch (err) {
|
|
32122
32568
|
if (err.code !== "ENOENT") {
|
|
32123
32569
|
console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
|
|
@@ -32129,8 +32575,8 @@ async function runSkill(opts) {
|
|
|
32129
32575
|
...existing,
|
|
32130
32576
|
mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
|
|
32131
32577
|
};
|
|
32132
|
-
await
|
|
32133
|
-
await
|
|
32578
|
+
await import_node_fs59.promises.mkdir(import_node_path96.default.dirname(target), { recursive: true });
|
|
32579
|
+
await import_node_fs59.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
32134
32580
|
console.log(`neat skill: wrote mcpServers.neat to ${target}`);
|
|
32135
32581
|
console.log("restart Claude Code to pick up the new MCP server.");
|
|
32136
32582
|
console.log("");
|
|
@@ -32174,6 +32620,16 @@ async function main() {
|
|
|
32174
32620
|
if (code !== 0) process.exit(code);
|
|
32175
32621
|
return;
|
|
32176
32622
|
}
|
|
32623
|
+
if (cmd0 === "login") {
|
|
32624
|
+
const code = await runLoginCommand(argv.slice(1));
|
|
32625
|
+
if (code !== 0) process.exit(code);
|
|
32626
|
+
return;
|
|
32627
|
+
}
|
|
32628
|
+
if (cmd0 === "logout") {
|
|
32629
|
+
const code = await runLogoutCommand(argv.slice(1));
|
|
32630
|
+
if (code !== 0) process.exit(code);
|
|
32631
|
+
return;
|
|
32632
|
+
}
|
|
32177
32633
|
if (cmd0 === "hooks") {
|
|
32178
32634
|
const code = await runHooksCommand(argv.slice(1));
|
|
32179
32635
|
if (code !== 0) process.exit(code);
|
|
@@ -32226,12 +32682,12 @@ async function main() {
|
|
|
32226
32682
|
console.error("neat init: --apply and --dry-run are mutually exclusive");
|
|
32227
32683
|
process.exit(2);
|
|
32228
32684
|
}
|
|
32229
|
-
const scanPath =
|
|
32685
|
+
const scanPath = import_node_path96.default.resolve(target);
|
|
32230
32686
|
const projectExplicit = parsed.project !== null;
|
|
32231
|
-
const projectName = projectExplicit ? project :
|
|
32687
|
+
const projectName = projectExplicit ? project : import_node_path96.default.basename(scanPath);
|
|
32232
32688
|
const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
|
|
32233
|
-
const fallback = pathsForProject(projectKey,
|
|
32234
|
-
const outPath =
|
|
32689
|
+
const fallback = pathsForProject(projectKey, import_node_path96.default.join(scanPath, "neat-out")).snapshotPath;
|
|
32690
|
+
const outPath = import_node_path96.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
|
|
32235
32691
|
const result = await runInit({
|
|
32236
32692
|
scanPath,
|
|
32237
32693
|
outPath,
|
|
@@ -32252,21 +32708,21 @@ async function main() {
|
|
|
32252
32708
|
usage5();
|
|
32253
32709
|
process.exit(2);
|
|
32254
32710
|
}
|
|
32255
|
-
const scanPath =
|
|
32256
|
-
const stat = await
|
|
32711
|
+
const scanPath = import_node_path96.default.resolve(target);
|
|
32712
|
+
const stat = await import_node_fs59.promises.stat(scanPath).catch(() => null);
|
|
32257
32713
|
if (!stat || !stat.isDirectory()) {
|
|
32258
32714
|
console.error(`neat watch: ${scanPath} is not a directory`);
|
|
32259
32715
|
process.exit(2);
|
|
32260
32716
|
}
|
|
32261
|
-
const projectPaths = pathsForProject(project,
|
|
32262
|
-
const outPath =
|
|
32263
|
-
const errorsPath =
|
|
32264
|
-
process.env.NEAT_ERRORS_PATH ??
|
|
32717
|
+
const projectPaths = pathsForProject(project, import_node_path96.default.join(scanPath, "neat-out"));
|
|
32718
|
+
const outPath = import_node_path96.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
|
|
32719
|
+
const errorsPath = import_node_path96.default.resolve(
|
|
32720
|
+
process.env.NEAT_ERRORS_PATH ?? import_node_path96.default.join(import_node_path96.default.dirname(outPath), import_node_path96.default.basename(projectPaths.errorsPath))
|
|
32265
32721
|
);
|
|
32266
|
-
const staleEventsPath =
|
|
32267
|
-
process.env.NEAT_STALE_EVENTS_PATH ??
|
|
32722
|
+
const staleEventsPath = import_node_path96.default.resolve(
|
|
32723
|
+
process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path96.default.join(import_node_path96.default.dirname(outPath), import_node_path96.default.basename(projectPaths.staleEventsPath))
|
|
32268
32724
|
);
|
|
32269
|
-
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ?
|
|
32725
|
+
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path96.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
|
|
32270
32726
|
const handle = await startWatch(getGraph(project), {
|
|
32271
32727
|
scanPath,
|
|
32272
32728
|
outPath,
|
|
@@ -32275,7 +32731,7 @@ async function main() {
|
|
|
32275
32731
|
project,
|
|
32276
32732
|
// Resolve NEAT_HOME so a `neat watch` picks up connectors added to
|
|
32277
32733
|
// ~/.neat/connectors.json (#871). Same resolution the rest of the CLI uses.
|
|
32278
|
-
neatHome: process.env.NEAT_HOME ?
|
|
32734
|
+
neatHome: process.env.NEAT_HOME ? import_node_path96.default.resolve(process.env.NEAT_HOME) : import_node_path96.default.join(import_node_os10.default.homedir(), ".neat"),
|
|
32279
32735
|
...embeddingsCachePath ? { embeddingsCachePath } : {},
|
|
32280
32736
|
host: process.env.HOST ?? "0.0.0.0",
|
|
32281
32737
|
port: Number(process.env.PORT ?? 8080),
|
|
@@ -32457,11 +32913,11 @@ async function main() {
|
|
|
32457
32913
|
process.exit(1);
|
|
32458
32914
|
}
|
|
32459
32915
|
async function tryOrchestrator(cmd, parsed) {
|
|
32460
|
-
const scanPath =
|
|
32461
|
-
const stat = await
|
|
32916
|
+
const scanPath = import_node_path96.default.resolve(cmd);
|
|
32917
|
+
const stat = await import_node_fs59.promises.stat(scanPath).catch(() => null);
|
|
32462
32918
|
if (!stat || !stat.isDirectory()) return null;
|
|
32463
32919
|
const projectExplicit = parsed.project !== null;
|
|
32464
|
-
const projectName = projectExplicit ? parsed.project :
|
|
32920
|
+
const projectName = projectExplicit ? parsed.project : import_node_path96.default.basename(scanPath);
|
|
32465
32921
|
const result = await runOrchestrator({
|
|
32466
32922
|
scanPath,
|
|
32467
32923
|
project: projectName,
|
|
@@ -32524,19 +32980,50 @@ Pass --project <name> to choose:
|
|
|
32524
32980
|
${names}`
|
|
32525
32981
|
);
|
|
32526
32982
|
}
|
|
32527
|
-
|
|
32528
|
-
|
|
32529
|
-
|
|
32530
|
-
|
|
32531
|
-
|
|
32532
|
-
|
|
32983
|
+
var UnknownProfileError = class extends Error {
|
|
32984
|
+
constructor(name) {
|
|
32985
|
+
super(
|
|
32986
|
+
`no profile named "${name}" in ~/.neat/profiles.json \u2014 run \`neat login\` first, or drop --profile / NEAT_PROFILE`
|
|
32987
|
+
);
|
|
32988
|
+
this.name = "UnknownProfileError";
|
|
32533
32989
|
}
|
|
32534
|
-
|
|
32990
|
+
};
|
|
32991
|
+
async function resolveClientTarget(opts = {}) {
|
|
32992
|
+
const named2 = opts.profile ?? process.env.NEAT_PROFILE;
|
|
32993
|
+
if (named2 && named2.length > 0) {
|
|
32994
|
+
const profile = await resolveProfile(named2);
|
|
32995
|
+
if (!profile) throw new UnknownProfileError(named2);
|
|
32996
|
+
return { endpoint: profile.endpoint, authToken: profile.authToken, source: "profile" };
|
|
32997
|
+
}
|
|
32998
|
+
const pin = process.env.NEAT_API_URL ?? process.env.NEAT_CORE_URL;
|
|
32999
|
+
if (pin) return { endpoint: pin, authToken: resolveAuthToken(), source: "env" };
|
|
33000
|
+
const active = await getActiveProfile().catch(() => void 0);
|
|
33001
|
+
if (active) return { endpoint: active.endpoint, authToken: active.authToken, source: "active" };
|
|
33002
|
+
if (opts.project) {
|
|
33003
|
+
const daemon = await findDaemonByProject(opts.project);
|
|
33004
|
+
if (daemon) {
|
|
33005
|
+
return { endpoint: `http://localhost:${daemon.record.ports.rest}`, source: "daemon-record" };
|
|
33006
|
+
}
|
|
33007
|
+
}
|
|
33008
|
+
return { endpoint: "http://localhost:8080", source: "default" };
|
|
33009
|
+
}
|
|
33010
|
+
async function resolveDaemonUrl(project, profile) {
|
|
33011
|
+
return (await resolveClientTarget({ project, profile })).endpoint;
|
|
32535
33012
|
}
|
|
32536
33013
|
async function runQueryVerb(cmd, parsed) {
|
|
32537
33014
|
const requestedProject = resolveProjectFlag(parsed);
|
|
32538
|
-
|
|
32539
|
-
|
|
33015
|
+
let target;
|
|
33016
|
+
try {
|
|
33017
|
+
target = await resolveClientTarget({ project: requestedProject, profile: parsed.profile ?? void 0 });
|
|
33018
|
+
} catch (err) {
|
|
33019
|
+
if (err instanceof UnknownProfileError) {
|
|
33020
|
+
process.stderr.write(`${err.message}
|
|
33021
|
+
`);
|
|
33022
|
+
return 2;
|
|
33023
|
+
}
|
|
33024
|
+
throw err;
|
|
33025
|
+
}
|
|
33026
|
+
const client = createHttpClient(target.endpoint, target.authToken);
|
|
32540
33027
|
const positional = parsed.positional;
|
|
32541
33028
|
let makeWork;
|
|
32542
33029
|
switch (cmd) {
|
|
@@ -32700,7 +33187,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
32700
33187
|
const detail = err.responseBody.length > 0 ? err.responseBody : err.message;
|
|
32701
33188
|
console.error(`neat ${cmd}: ${detail.trim()}`);
|
|
32702
33189
|
} else if (err instanceof TransportError) {
|
|
32703
|
-
console.error(`neat ${cmd}: ${err.message}. Is the daemon running? (endpoint=${
|
|
33190
|
+
console.error(`neat ${cmd}: ${err.message}. Is the daemon running? (endpoint=${target.endpoint})`);
|
|
32704
33191
|
} else {
|
|
32705
33192
|
console.error(`neat ${cmd}: ${err.message}`);
|
|
32706
33193
|
}
|
|
@@ -32709,9 +33196,14 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
32709
33196
|
}
|
|
32710
33197
|
async function runMonitorVerb(parsed) {
|
|
32711
33198
|
const requestedProject = resolveProjectFlag(parsed);
|
|
32712
|
-
|
|
32713
|
-
|
|
32714
|
-
|
|
33199
|
+
let target;
|
|
33200
|
+
try {
|
|
33201
|
+
target = await resolveClientTarget({ project: requestedProject, profile: parsed.profile ?? void 0 });
|
|
33202
|
+
} catch (err) {
|
|
33203
|
+
if (err instanceof UnknownProfileError) return 0;
|
|
33204
|
+
throw err;
|
|
33205
|
+
}
|
|
33206
|
+
const client = createHttpClient(target.endpoint, target.authToken);
|
|
32715
33207
|
let project;
|
|
32716
33208
|
try {
|
|
32717
33209
|
project = await resolveProjectForVerb(client, parsed);
|
|
@@ -32727,10 +33219,10 @@ async function runMonitorVerb(parsed) {
|
|
|
32727
33219
|
process.once("SIGTERM", onSignal);
|
|
32728
33220
|
try {
|
|
32729
33221
|
return await runMonitor({
|
|
32730
|
-
baseUrl,
|
|
33222
|
+
baseUrl: target.endpoint,
|
|
32731
33223
|
project,
|
|
32732
33224
|
json: parsed.json,
|
|
32733
|
-
authToken:
|
|
33225
|
+
authToken: target.authToken,
|
|
32734
33226
|
signal: controller.signal
|
|
32735
33227
|
});
|
|
32736
33228
|
} finally {
|
|
@@ -32750,12 +33242,14 @@ if (/[\\/](?:cli\.(?:cjs|js)|cli|neat|neat\.is)$/.test(entry)) {
|
|
|
32750
33242
|
CLAUDE_SKILL_CONFIG,
|
|
32751
33243
|
ProjectResolutionError,
|
|
32752
33244
|
QUERY_VERBS,
|
|
33245
|
+
UnknownProfileError,
|
|
32753
33246
|
commandPrefix,
|
|
32754
33247
|
isNpxInvocation,
|
|
32755
33248
|
main,
|
|
32756
33249
|
parseArgs,
|
|
32757
33250
|
printBanner,
|
|
32758
33251
|
readPackageVersion,
|
|
33252
|
+
resolveClientTarget,
|
|
32759
33253
|
resolveDaemonUrl,
|
|
32760
33254
|
resolveProjectForVerb,
|
|
32761
33255
|
runInit,
|