@neat.is/core 0.4.5 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-6GXBAR3M.js → chunk-NON5AXOR.js} +36 -2
- package/dist/chunk-NON5AXOR.js.map +1 -0
- package/dist/cli.cjs +198 -74
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +187 -64
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +34 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/neatd.cjs +34 -0
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-6GXBAR3M.js.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -49,9 +49,9 @@ function mountBearerAuth(app, opts) {
|
|
|
49
49
|
const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
|
|
50
50
|
const publicRead = opts.publicRead === true;
|
|
51
51
|
app.addHook("preHandler", (req, reply, done) => {
|
|
52
|
-
const
|
|
52
|
+
const path46 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
53
53
|
for (const suffix of suffixes) {
|
|
54
|
-
if (
|
|
54
|
+
if (path46 === suffix || path46.endsWith(suffix)) {
|
|
55
55
|
done();
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
@@ -562,8 +562,8 @@ __export(cli_exports, {
|
|
|
562
562
|
});
|
|
563
563
|
module.exports = __toCommonJS(cli_exports);
|
|
564
564
|
init_cjs_shims();
|
|
565
|
-
var
|
|
566
|
-
var
|
|
565
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
566
|
+
var import_node_fs29 = require("fs");
|
|
567
567
|
var import_node_url3 = require("url");
|
|
568
568
|
|
|
569
569
|
// src/graph.ts
|
|
@@ -1074,19 +1074,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1074
1074
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1075
1075
|
let best = { path: [start], edges: [] };
|
|
1076
1076
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1077
|
-
function step(node,
|
|
1078
|
-
if (
|
|
1079
|
-
best = { path: [...
|
|
1077
|
+
function step(node, path46, edges) {
|
|
1078
|
+
if (path46.length > best.path.length) {
|
|
1079
|
+
best = { path: [...path46], edges: [...edges] };
|
|
1080
1080
|
}
|
|
1081
|
-
if (
|
|
1081
|
+
if (path46.length - 1 >= maxDepth) return;
|
|
1082
1082
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1083
1083
|
for (const [srcId, edge] of incoming) {
|
|
1084
1084
|
if (visited.has(srcId)) continue;
|
|
1085
1085
|
visited.add(srcId);
|
|
1086
|
-
|
|
1086
|
+
path46.push(srcId);
|
|
1087
1087
|
edges.push(edge);
|
|
1088
|
-
step(srcId,
|
|
1089
|
-
|
|
1088
|
+
step(srcId, path46, edges);
|
|
1089
|
+
path46.pop();
|
|
1090
1090
|
edges.pop();
|
|
1091
1091
|
visited.delete(srcId);
|
|
1092
1092
|
}
|
|
@@ -6753,13 +6753,20 @@ var SDK_PACKAGES = [
|
|
|
6753
6753
|
{ name: "@opentelemetry/sdk-node", version: "^0.57.0" },
|
|
6754
6754
|
{ name: "@opentelemetry/auto-instrumentations-node", version: "^0.55.0" }
|
|
6755
6755
|
];
|
|
6756
|
+
function getMajor(versionRange) {
|
|
6757
|
+
if (!versionRange) return 0;
|
|
6758
|
+
const match = versionRange.match(/(\d+)/);
|
|
6759
|
+
return match ? parseInt(match[1], 10) : 0;
|
|
6760
|
+
}
|
|
6756
6761
|
function detectNonBundledInstrumentations(pkg) {
|
|
6757
6762
|
const deps = allDeps(pkg);
|
|
6758
6763
|
const out = [];
|
|
6759
6764
|
if ("@prisma/client" in deps) {
|
|
6765
|
+
const prismaMajor = getMajor(deps["@prisma/client"]);
|
|
6766
|
+
const prismaInstrVersion = prismaMajor >= 6 ? "^6.0.0" : "^5.0.0";
|
|
6760
6767
|
out.push({
|
|
6761
6768
|
pkg: "@prisma/instrumentation",
|
|
6762
|
-
version:
|
|
6769
|
+
version: prismaInstrVersion,
|
|
6763
6770
|
registration: "instrumentations.push(new (require('@prisma/instrumentation').PrismaInstrumentation)())"
|
|
6764
6771
|
});
|
|
6765
6772
|
}
|
|
@@ -8088,19 +8095,100 @@ function renderPatch(sections) {
|
|
|
8088
8095
|
|
|
8089
8096
|
// src/orchestrator.ts
|
|
8090
8097
|
init_cjs_shims();
|
|
8091
|
-
var
|
|
8098
|
+
var import_node_fs28 = require("fs");
|
|
8092
8099
|
var import_node_http = __toESM(require("http"), 1);
|
|
8093
8100
|
var import_node_net = __toESM(require("net"), 1);
|
|
8101
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
8102
|
+
var import_node_child_process3 = require("child_process");
|
|
8103
|
+
var import_node_readline = __toESM(require("readline"), 1);
|
|
8104
|
+
|
|
8105
|
+
// src/installers/package-manager.ts
|
|
8106
|
+
init_cjs_shims();
|
|
8107
|
+
var import_node_fs27 = require("fs");
|
|
8094
8108
|
var import_node_path42 = __toESM(require("path"), 1);
|
|
8095
8109
|
var import_node_child_process2 = require("child_process");
|
|
8096
|
-
var
|
|
8110
|
+
var LOCKFILE_PRIORITY = [
|
|
8111
|
+
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
8112
|
+
{ lockfile: "pnpm-lock.yaml", pm: "pnpm", args: ["install", "--no-summary"] },
|
|
8113
|
+
{ lockfile: "yarn.lock", pm: "yarn", args: ["install", "--silent"] },
|
|
8114
|
+
{
|
|
8115
|
+
lockfile: "package-lock.json",
|
|
8116
|
+
pm: "npm",
|
|
8117
|
+
args: ["install", "--no-audit", "--no-fund", "--prefer-offline"]
|
|
8118
|
+
}
|
|
8119
|
+
];
|
|
8120
|
+
var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
|
|
8121
|
+
async function exists4(p) {
|
|
8122
|
+
try {
|
|
8123
|
+
await import_node_fs27.promises.access(p);
|
|
8124
|
+
return true;
|
|
8125
|
+
} catch {
|
|
8126
|
+
return false;
|
|
8127
|
+
}
|
|
8128
|
+
}
|
|
8129
|
+
async function detectPackageManager(serviceDir) {
|
|
8130
|
+
let dir = import_node_path42.default.resolve(serviceDir);
|
|
8131
|
+
const stops = /* @__PURE__ */ new Set();
|
|
8132
|
+
for (let i = 0; i < 64; i++) {
|
|
8133
|
+
if (stops.has(dir)) break;
|
|
8134
|
+
stops.add(dir);
|
|
8135
|
+
for (const candidate of LOCKFILE_PRIORITY) {
|
|
8136
|
+
const lockPath = import_node_path42.default.join(dir, candidate.lockfile);
|
|
8137
|
+
if (await exists4(lockPath)) {
|
|
8138
|
+
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
8139
|
+
}
|
|
8140
|
+
}
|
|
8141
|
+
const parent = import_node_path42.default.dirname(dir);
|
|
8142
|
+
if (parent === dir) break;
|
|
8143
|
+
dir = parent;
|
|
8144
|
+
}
|
|
8145
|
+
return { pm: "npm", cwd: import_node_path42.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
8146
|
+
}
|
|
8147
|
+
async function runPackageManagerInstall(cmd) {
|
|
8148
|
+
return new Promise((resolve) => {
|
|
8149
|
+
const child = (0, import_node_child_process2.spawn)(cmd.pm, cmd.args, {
|
|
8150
|
+
cwd: cmd.cwd,
|
|
8151
|
+
// Inherit PATH + HOME so the user's installed managers resolve.
|
|
8152
|
+
env: process.env,
|
|
8153
|
+
// `false` keeps the parent in control of cleanup if the orchestrator
|
|
8154
|
+
// exits before install finishes. Cross-platform-safe.
|
|
8155
|
+
shell: false,
|
|
8156
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
8157
|
+
});
|
|
8158
|
+
let stderr = "";
|
|
8159
|
+
child.stderr?.on("data", (chunk) => {
|
|
8160
|
+
stderr += chunk.toString("utf8");
|
|
8161
|
+
});
|
|
8162
|
+
child.on("error", (err) => {
|
|
8163
|
+
resolve({
|
|
8164
|
+
pm: cmd.pm,
|
|
8165
|
+
cwd: cmd.cwd,
|
|
8166
|
+
args: cmd.args,
|
|
8167
|
+
exitCode: 127,
|
|
8168
|
+
stderr: stderr + `
|
|
8169
|
+
${err.message}`
|
|
8170
|
+
});
|
|
8171
|
+
});
|
|
8172
|
+
child.on("close", (code) => {
|
|
8173
|
+
resolve({
|
|
8174
|
+
pm: cmd.pm,
|
|
8175
|
+
cwd: cmd.cwd,
|
|
8176
|
+
args: cmd.args,
|
|
8177
|
+
exitCode: code ?? 1,
|
|
8178
|
+
stderr: stderr.trim()
|
|
8179
|
+
});
|
|
8180
|
+
});
|
|
8181
|
+
});
|
|
8182
|
+
}
|
|
8183
|
+
|
|
8184
|
+
// src/orchestrator.ts
|
|
8097
8185
|
async function extractAndPersist(opts) {
|
|
8098
8186
|
const services = await discoverServices(opts.scanPath);
|
|
8099
8187
|
const languages = [...new Set(services.map((s) => s.node.language))].sort();
|
|
8100
8188
|
const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
|
|
8101
8189
|
resetGraph(graphKey);
|
|
8102
8190
|
const graph = getGraph(graphKey);
|
|
8103
|
-
const projectPaths = pathsForProject(graphKey,
|
|
8191
|
+
const projectPaths = pathsForProject(graphKey, import_node_path43.default.join(opts.scanPath, "neat-out"));
|
|
8104
8192
|
const extraction = await extractFromDirectory(graph, opts.scanPath, {
|
|
8105
8193
|
errorsPath: projectPaths.errorsPath
|
|
8106
8194
|
});
|
|
@@ -8118,12 +8206,15 @@ async function extractAndPersist(opts) {
|
|
|
8118
8206
|
errorsPath: projectPaths.errorsPath
|
|
8119
8207
|
};
|
|
8120
8208
|
}
|
|
8121
|
-
async function applyInstallersOver(services, project) {
|
|
8209
|
+
async function applyInstallersOver(services, project, options = {}) {
|
|
8210
|
+
const resolveManager = options.resolveManager ?? detectPackageManager;
|
|
8211
|
+
const runInstall = options.runInstall ?? runPackageManagerInstall;
|
|
8122
8212
|
let instrumented = 0;
|
|
8123
8213
|
let already = 0;
|
|
8124
8214
|
let libOnly = 0;
|
|
8125
8215
|
let browserBundle = 0;
|
|
8126
8216
|
let reactNative = 0;
|
|
8217
|
+
const installPlans = /* @__PURE__ */ new Map();
|
|
8127
8218
|
for (const svc of services) {
|
|
8128
8219
|
const installer = await pickInstaller(svc.dir);
|
|
8129
8220
|
if (!installer) continue;
|
|
@@ -8133,8 +8224,14 @@ async function applyInstallersOver(services, project) {
|
|
|
8133
8224
|
continue;
|
|
8134
8225
|
}
|
|
8135
8226
|
const outcome = await installer.apply(plan3);
|
|
8136
|
-
if (outcome.outcome === "instrumented")
|
|
8137
|
-
|
|
8227
|
+
if (outcome.outcome === "instrumented") {
|
|
8228
|
+
instrumented++;
|
|
8229
|
+
if (plan3.dependencyEdits.length > 0) {
|
|
8230
|
+
const cmd = await resolveManager(svc.dir);
|
|
8231
|
+
const key = `${cmd.pm}:${cmd.cwd}`;
|
|
8232
|
+
if (!installPlans.has(key)) installPlans.set(key, cmd);
|
|
8233
|
+
}
|
|
8234
|
+
} else if (outcome.outcome === "already-instrumented") already++;
|
|
8138
8235
|
else if (outcome.outcome === "lib-only") libOnly++;
|
|
8139
8236
|
else if (outcome.outcome === "browser-bundle") {
|
|
8140
8237
|
browserBundle++;
|
|
@@ -8144,7 +8241,30 @@ async function applyInstallersOver(services, project) {
|
|
|
8144
8241
|
console.log(`skipping ${svc.dir}: React Native target; browser-OTel support lands in a future release.`);
|
|
8145
8242
|
}
|
|
8146
8243
|
}
|
|
8147
|
-
|
|
8244
|
+
const packageManagerInstalls = [];
|
|
8245
|
+
for (const cmd of installPlans.values()) {
|
|
8246
|
+
console.log(`running \`${cmd.pm} ${cmd.args.join(" ")}\` in ${cmd.cwd}`);
|
|
8247
|
+
const result = await runInstall(cmd);
|
|
8248
|
+
packageManagerInstalls.push(result);
|
|
8249
|
+
if (result.exitCode !== 0) {
|
|
8250
|
+
console.error(
|
|
8251
|
+
`neat: ${cmd.pm} install failed in ${cmd.cwd} (exit ${result.exitCode}); run it manually to surface the error.`
|
|
8252
|
+
);
|
|
8253
|
+
if (result.stderr.length > 0) {
|
|
8254
|
+
for (const line of result.stderr.split(/\r?\n/).slice(0, 20)) {
|
|
8255
|
+
console.error(` ${line}`);
|
|
8256
|
+
}
|
|
8257
|
+
}
|
|
8258
|
+
}
|
|
8259
|
+
}
|
|
8260
|
+
return {
|
|
8261
|
+
instrumented,
|
|
8262
|
+
alreadyInstrumented: already,
|
|
8263
|
+
libOnly,
|
|
8264
|
+
browserBundle,
|
|
8265
|
+
reactNative,
|
|
8266
|
+
packageManagerInstalls
|
|
8267
|
+
};
|
|
8148
8268
|
}
|
|
8149
8269
|
async function promptYesNo(question) {
|
|
8150
8270
|
const rl = import_node_readline.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -8280,10 +8400,10 @@ function formatPortCollisionMessage(port) {
|
|
|
8280
8400
|
];
|
|
8281
8401
|
}
|
|
8282
8402
|
function spawnDaemonDetached() {
|
|
8283
|
-
const here =
|
|
8403
|
+
const here = import_node_path43.default.dirname(new URL(importMetaUrl).pathname);
|
|
8284
8404
|
const candidates = [
|
|
8285
|
-
|
|
8286
|
-
|
|
8405
|
+
import_node_path43.default.join(here, "neatd.cjs"),
|
|
8406
|
+
import_node_path43.default.join(here, "neatd.js")
|
|
8287
8407
|
];
|
|
8288
8408
|
let entry2 = null;
|
|
8289
8409
|
const fsSync = require("fs");
|
|
@@ -8303,7 +8423,7 @@ function spawnDaemonDetached() {
|
|
|
8303
8423
|
if (!hasToken && (!env.HOST || env.HOST.length === 0)) {
|
|
8304
8424
|
env.HOST = "127.0.0.1";
|
|
8305
8425
|
}
|
|
8306
|
-
const child = (0,
|
|
8426
|
+
const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
|
|
8307
8427
|
detached: true,
|
|
8308
8428
|
// stderr inherits the orchestrator's fd so the daemon's
|
|
8309
8429
|
// `BindAuthorityError` message lands in front of the operator instead
|
|
@@ -8320,7 +8440,7 @@ function openBrowser(url) {
|
|
|
8320
8440
|
const cmd = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
8321
8441
|
const args = platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
8322
8442
|
try {
|
|
8323
|
-
const child = (0,
|
|
8443
|
+
const child = (0, import_node_child_process3.spawn)(cmd, args, { detached: true, stdio: "ignore" });
|
|
8324
8444
|
child.on("error", () => {
|
|
8325
8445
|
});
|
|
8326
8446
|
child.unref();
|
|
@@ -8341,7 +8461,7 @@ async function runOrchestrator(opts) {
|
|
|
8341
8461
|
browser: "skipped"
|
|
8342
8462
|
}
|
|
8343
8463
|
};
|
|
8344
|
-
const stat = await
|
|
8464
|
+
const stat = await import_node_fs28.promises.stat(opts.scanPath).catch(() => null);
|
|
8345
8465
|
if (!stat || !stat.isDirectory()) {
|
|
8346
8466
|
console.error(`neat: ${opts.scanPath} is not a directory`);
|
|
8347
8467
|
result.exitCode = 2;
|
|
@@ -8409,6 +8529,10 @@ async function runOrchestrator(opts) {
|
|
|
8409
8529
|
console.log(
|
|
8410
8530
|
`instrumented ${tally.instrumented}, already ${tally.alreadyInstrumented}, lib-only ${tally.libOnly}`
|
|
8411
8531
|
);
|
|
8532
|
+
const failedInstalls = tally.packageManagerInstalls.filter((i) => i.exitCode !== 0);
|
|
8533
|
+
if (failedInstalls.length > 0) {
|
|
8534
|
+
result.exitCode = 1;
|
|
8535
|
+
}
|
|
8412
8536
|
}
|
|
8413
8537
|
const restPort = Number(process.env.PORT ?? 8080);
|
|
8414
8538
|
const timeoutMs = opts.daemonReadyTimeoutMs ?? DEFAULT_DAEMON_READY_TIMEOUT_MS;
|
|
@@ -8480,7 +8604,7 @@ function printSummary(result, graph, dashboardUrl) {
|
|
|
8480
8604
|
|
|
8481
8605
|
// src/cli-verbs.ts
|
|
8482
8606
|
init_cjs_shims();
|
|
8483
|
-
var
|
|
8607
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
8484
8608
|
|
|
8485
8609
|
// src/cli-client.ts
|
|
8486
8610
|
init_cjs_shims();
|
|
@@ -8505,10 +8629,10 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
8505
8629
|
const root = baseUrl.replace(/\/$/, "");
|
|
8506
8630
|
const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
|
|
8507
8631
|
return {
|
|
8508
|
-
async get(
|
|
8632
|
+
async get(path46) {
|
|
8509
8633
|
let res;
|
|
8510
8634
|
try {
|
|
8511
|
-
res = await fetch(`${root}${
|
|
8635
|
+
res = await fetch(`${root}${path46}`, {
|
|
8512
8636
|
headers: { ...authHeader }
|
|
8513
8637
|
});
|
|
8514
8638
|
} catch (err) {
|
|
@@ -8520,16 +8644,16 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
8520
8644
|
const body = await res.text().catch(() => "");
|
|
8521
8645
|
throw new HttpError(
|
|
8522
8646
|
res.status,
|
|
8523
|
-
`${res.status} ${res.statusText} on GET ${
|
|
8647
|
+
`${res.status} ${res.statusText} on GET ${path46}: ${body}`,
|
|
8524
8648
|
body
|
|
8525
8649
|
);
|
|
8526
8650
|
}
|
|
8527
8651
|
return await res.json();
|
|
8528
8652
|
},
|
|
8529
|
-
async post(
|
|
8653
|
+
async post(path46, body) {
|
|
8530
8654
|
let res;
|
|
8531
8655
|
try {
|
|
8532
|
-
res = await fetch(`${root}${
|
|
8656
|
+
res = await fetch(`${root}${path46}`, {
|
|
8533
8657
|
method: "POST",
|
|
8534
8658
|
headers: { "content-type": "application/json", ...authHeader },
|
|
8535
8659
|
body: JSON.stringify(body)
|
|
@@ -8543,7 +8667,7 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
8543
8667
|
const text = await res.text().catch(() => "");
|
|
8544
8668
|
throw new HttpError(
|
|
8545
8669
|
res.status,
|
|
8546
|
-
`${res.status} ${res.statusText} on POST ${
|
|
8670
|
+
`${res.status} ${res.statusText} on POST ${path46}: ${text}`,
|
|
8547
8671
|
text
|
|
8548
8672
|
);
|
|
8549
8673
|
}
|
|
@@ -8557,12 +8681,12 @@ function projectPath(project, suffix) {
|
|
|
8557
8681
|
}
|
|
8558
8682
|
async function runRootCause(client, input) {
|
|
8559
8683
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
8560
|
-
const
|
|
8684
|
+
const path46 = projectPath(
|
|
8561
8685
|
input.project,
|
|
8562
8686
|
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
8563
8687
|
);
|
|
8564
8688
|
try {
|
|
8565
|
-
const result = await client.get(
|
|
8689
|
+
const result = await client.get(path46);
|
|
8566
8690
|
const arrowPath = result.traversalPath.join(" \u2190 ");
|
|
8567
8691
|
const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
|
|
8568
8692
|
const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
|
|
@@ -8588,12 +8712,12 @@ async function runRootCause(client, input) {
|
|
|
8588
8712
|
}
|
|
8589
8713
|
async function runBlastRadius(client, input) {
|
|
8590
8714
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
8591
|
-
const
|
|
8715
|
+
const path46 = projectPath(
|
|
8592
8716
|
input.project,
|
|
8593
8717
|
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
8594
8718
|
);
|
|
8595
8719
|
try {
|
|
8596
|
-
const result = await client.get(
|
|
8720
|
+
const result = await client.get(path46);
|
|
8597
8721
|
if (result.totalAffected === 0) {
|
|
8598
8722
|
return {
|
|
8599
8723
|
summary: `${result.origin} has no downstream dependencies. Nothing else would break if it failed.`
|
|
@@ -8627,12 +8751,12 @@ function formatBlastEntry(n) {
|
|
|
8627
8751
|
}
|
|
8628
8752
|
async function runDependencies(client, input) {
|
|
8629
8753
|
const depth = input.depth ?? 3;
|
|
8630
|
-
const
|
|
8754
|
+
const path46 = projectPath(
|
|
8631
8755
|
input.project,
|
|
8632
8756
|
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
8633
8757
|
);
|
|
8634
8758
|
try {
|
|
8635
|
-
const result = await client.get(
|
|
8759
|
+
const result = await client.get(path46);
|
|
8636
8760
|
if (result.total === 0) {
|
|
8637
8761
|
return {
|
|
8638
8762
|
summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
|
|
@@ -8713,9 +8837,9 @@ function formatDuration(ms) {
|
|
|
8713
8837
|
return `${Math.round(h / 24)}d`;
|
|
8714
8838
|
}
|
|
8715
8839
|
async function runIncidents(client, input) {
|
|
8716
|
-
const
|
|
8840
|
+
const path46 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
8717
8841
|
try {
|
|
8718
|
-
const body = await client.get(
|
|
8842
|
+
const body = await client.get(path46);
|
|
8719
8843
|
const events = body.events;
|
|
8720
8844
|
if (events.length === 0) {
|
|
8721
8845
|
return {
|
|
@@ -9005,7 +9129,7 @@ async function resolveProjectEntry(opts) {
|
|
|
9005
9129
|
const cwd = opts.cwd ?? process.cwd();
|
|
9006
9130
|
const resolvedCwd = await normalizeProjectPath(cwd);
|
|
9007
9131
|
for (const entry2 of entries) {
|
|
9008
|
-
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${
|
|
9132
|
+
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path44.default.sep}`)) {
|
|
9009
9133
|
return entry2;
|
|
9010
9134
|
}
|
|
9011
9135
|
}
|
|
@@ -9365,14 +9489,14 @@ function assignFlag(out, field, value) {
|
|
|
9365
9489
|
out[field] = value;
|
|
9366
9490
|
}
|
|
9367
9491
|
function readPackageVersion() {
|
|
9368
|
-
const here = typeof __dirname !== "undefined" ? __dirname :
|
|
9492
|
+
const here = typeof __dirname !== "undefined" ? __dirname : import_node_path45.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
|
|
9369
9493
|
const candidates = [
|
|
9370
|
-
|
|
9371
|
-
|
|
9494
|
+
import_node_path45.default.resolve(here, "../package.json"),
|
|
9495
|
+
import_node_path45.default.resolve(here, "../../package.json")
|
|
9372
9496
|
];
|
|
9373
9497
|
for (const candidate of candidates) {
|
|
9374
9498
|
try {
|
|
9375
|
-
const raw = (0,
|
|
9499
|
+
const raw = (0, import_node_fs29.readFileSync)(candidate, "utf8");
|
|
9376
9500
|
const parsed = JSON.parse(raw);
|
|
9377
9501
|
if (parsed.name === "@neat.is/core" && typeof parsed.version === "string") {
|
|
9378
9502
|
return parsed.version;
|
|
@@ -9436,7 +9560,7 @@ async function buildPatchSections(services, project) {
|
|
|
9436
9560
|
}
|
|
9437
9561
|
async function runInit(opts) {
|
|
9438
9562
|
const written = [];
|
|
9439
|
-
const stat = await
|
|
9563
|
+
const stat = await import_node_fs29.promises.stat(opts.scanPath).catch(() => null);
|
|
9440
9564
|
if (!stat || !stat.isDirectory()) {
|
|
9441
9565
|
console.error(`neat init: ${opts.scanPath} is not a directory`);
|
|
9442
9566
|
return { exitCode: 2, writtenFiles: written };
|
|
@@ -9445,13 +9569,13 @@ async function runInit(opts) {
|
|
|
9445
9569
|
printDiscoveryReport(opts, services);
|
|
9446
9570
|
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
9447
9571
|
const patch = renderPatch(sections);
|
|
9448
|
-
const patchPath =
|
|
9572
|
+
const patchPath = import_node_path45.default.join(opts.scanPath, "neat.patch");
|
|
9449
9573
|
if (opts.dryRun) {
|
|
9450
|
-
await
|
|
9574
|
+
await import_node_fs29.promises.writeFile(patchPath, patch, "utf8");
|
|
9451
9575
|
written.push(patchPath);
|
|
9452
9576
|
console.log(`dry-run: patch written to ${patchPath}`);
|
|
9453
|
-
const gitignorePath =
|
|
9454
|
-
const gitignoreExists = await
|
|
9577
|
+
const gitignorePath = import_node_path45.default.join(opts.scanPath, ".gitignore");
|
|
9578
|
+
const gitignoreExists = await import_node_fs29.promises.stat(gitignorePath).then(() => true).catch(() => false);
|
|
9455
9579
|
const verb = gitignoreExists ? "append" : "create";
|
|
9456
9580
|
console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
|
|
9457
9581
|
console.log("rerun without --dry-run to register and snapshot.");
|
|
@@ -9462,9 +9586,9 @@ async function runInit(opts) {
|
|
|
9462
9586
|
const graph = getGraph(graphKey);
|
|
9463
9587
|
const projectPaths = pathsForProject(
|
|
9464
9588
|
graphKey,
|
|
9465
|
-
|
|
9589
|
+
import_node_path45.default.join(opts.scanPath, "neat-out")
|
|
9466
9590
|
);
|
|
9467
|
-
const errorsPath =
|
|
9591
|
+
const errorsPath = import_node_path45.default.join(import_node_path45.default.dirname(opts.outPath), import_node_path45.default.basename(projectPaths.errorsPath));
|
|
9468
9592
|
const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
|
|
9469
9593
|
await saveGraphToDisk(graph, opts.outPath);
|
|
9470
9594
|
written.push(opts.outPath);
|
|
@@ -9543,7 +9667,7 @@ async function runInit(opts) {
|
|
|
9543
9667
|
console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
|
|
9544
9668
|
}
|
|
9545
9669
|
} else {
|
|
9546
|
-
await
|
|
9670
|
+
await import_node_fs29.promises.writeFile(patchPath, patch, "utf8");
|
|
9547
9671
|
written.push(patchPath);
|
|
9548
9672
|
}
|
|
9549
9673
|
}
|
|
@@ -9583,9 +9707,9 @@ var CLAUDE_SKILL_CONFIG = {
|
|
|
9583
9707
|
};
|
|
9584
9708
|
function claudeConfigPath() {
|
|
9585
9709
|
const override = process.env.NEAT_CLAUDE_CONFIG;
|
|
9586
|
-
if (override && override.length > 0) return
|
|
9710
|
+
if (override && override.length > 0) return import_node_path45.default.resolve(override);
|
|
9587
9711
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
9588
|
-
return
|
|
9712
|
+
return import_node_path45.default.join(home, ".claude.json");
|
|
9589
9713
|
}
|
|
9590
9714
|
async function runSkill(opts) {
|
|
9591
9715
|
const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
|
|
@@ -9597,7 +9721,7 @@ async function runSkill(opts) {
|
|
|
9597
9721
|
const target = claudeConfigPath();
|
|
9598
9722
|
let existing = {};
|
|
9599
9723
|
try {
|
|
9600
|
-
existing = JSON.parse(await
|
|
9724
|
+
existing = JSON.parse(await import_node_fs29.promises.readFile(target, "utf8"));
|
|
9601
9725
|
} catch (err) {
|
|
9602
9726
|
if (err.code !== "ENOENT") {
|
|
9603
9727
|
console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
|
|
@@ -9609,8 +9733,8 @@ async function runSkill(opts) {
|
|
|
9609
9733
|
...existing,
|
|
9610
9734
|
mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
|
|
9611
9735
|
};
|
|
9612
|
-
await
|
|
9613
|
-
await
|
|
9736
|
+
await import_node_fs29.promises.mkdir(import_node_path45.default.dirname(target), { recursive: true });
|
|
9737
|
+
await import_node_fs29.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
9614
9738
|
console.log(`neat skill: wrote mcpServers.neat to ${target}`);
|
|
9615
9739
|
console.log("restart Claude Code to pick up the new MCP server.");
|
|
9616
9740
|
return { exitCode: 0 };
|
|
@@ -9648,12 +9772,12 @@ async function main() {
|
|
|
9648
9772
|
console.error("neat init: --apply and --dry-run are mutually exclusive");
|
|
9649
9773
|
process.exit(2);
|
|
9650
9774
|
}
|
|
9651
|
-
const scanPath =
|
|
9775
|
+
const scanPath = import_node_path45.default.resolve(target);
|
|
9652
9776
|
const projectExplicit = parsed.project !== null;
|
|
9653
|
-
const projectName = projectExplicit ? project :
|
|
9777
|
+
const projectName = projectExplicit ? project : import_node_path45.default.basename(scanPath);
|
|
9654
9778
|
const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
|
|
9655
|
-
const fallback = pathsForProject(projectKey,
|
|
9656
|
-
const outPath =
|
|
9779
|
+
const fallback = pathsForProject(projectKey, import_node_path45.default.join(scanPath, "neat-out")).snapshotPath;
|
|
9780
|
+
const outPath = import_node_path45.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
|
|
9657
9781
|
const result = await runInit({
|
|
9658
9782
|
scanPath,
|
|
9659
9783
|
outPath,
|
|
@@ -9674,21 +9798,21 @@ async function main() {
|
|
|
9674
9798
|
usage();
|
|
9675
9799
|
process.exit(2);
|
|
9676
9800
|
}
|
|
9677
|
-
const scanPath =
|
|
9678
|
-
const stat = await
|
|
9801
|
+
const scanPath = import_node_path45.default.resolve(target);
|
|
9802
|
+
const stat = await import_node_fs29.promises.stat(scanPath).catch(() => null);
|
|
9679
9803
|
if (!stat || !stat.isDirectory()) {
|
|
9680
9804
|
console.error(`neat watch: ${scanPath} is not a directory`);
|
|
9681
9805
|
process.exit(2);
|
|
9682
9806
|
}
|
|
9683
|
-
const projectPaths = pathsForProject(project,
|
|
9684
|
-
const outPath =
|
|
9685
|
-
const errorsPath =
|
|
9686
|
-
process.env.NEAT_ERRORS_PATH ??
|
|
9807
|
+
const projectPaths = pathsForProject(project, import_node_path45.default.join(scanPath, "neat-out"));
|
|
9808
|
+
const outPath = import_node_path45.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
|
|
9809
|
+
const errorsPath = import_node_path45.default.resolve(
|
|
9810
|
+
process.env.NEAT_ERRORS_PATH ?? import_node_path45.default.join(import_node_path45.default.dirname(outPath), import_node_path45.default.basename(projectPaths.errorsPath))
|
|
9687
9811
|
);
|
|
9688
|
-
const staleEventsPath =
|
|
9689
|
-
process.env.NEAT_STALE_EVENTS_PATH ??
|
|
9812
|
+
const staleEventsPath = import_node_path45.default.resolve(
|
|
9813
|
+
process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path45.default.join(import_node_path45.default.dirname(outPath), import_node_path45.default.basename(projectPaths.staleEventsPath))
|
|
9690
9814
|
);
|
|
9691
|
-
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ?
|
|
9815
|
+
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path45.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
|
|
9692
9816
|
const handle = await startWatch(getGraph(project), {
|
|
9693
9817
|
scanPath,
|
|
9694
9818
|
outPath,
|
|
@@ -9834,11 +9958,11 @@ async function main() {
|
|
|
9834
9958
|
process.exit(1);
|
|
9835
9959
|
}
|
|
9836
9960
|
async function tryOrchestrator(cmd, parsed) {
|
|
9837
|
-
const scanPath =
|
|
9838
|
-
const stat = await
|
|
9961
|
+
const scanPath = import_node_path45.default.resolve(cmd);
|
|
9962
|
+
const stat = await import_node_fs29.promises.stat(scanPath).catch(() => null);
|
|
9839
9963
|
if (!stat || !stat.isDirectory()) return null;
|
|
9840
9964
|
const projectExplicit = parsed.project !== null;
|
|
9841
|
-
const projectName = projectExplicit ? parsed.project :
|
|
9965
|
+
const projectName = projectExplicit ? parsed.project : import_node_path45.default.basename(scanPath);
|
|
9842
9966
|
const result = await runOrchestrator({
|
|
9843
9967
|
scanPath,
|
|
9844
9968
|
project: projectName,
|