@neat.is/core 0.4.0 → 0.4.3
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-N6RPINEJ.js → chunk-CB2UK4EH.js} +138 -41
- package/dist/chunk-CB2UK4EH.js.map +1 -0
- package/dist/{chunk-IXIFJKMM.js → chunk-D5PIJFBE.js} +2 -2
- package/dist/{chunk-4V23KYOP.js → chunk-KYRIQIPG.js} +17 -9
- package/dist/chunk-KYRIQIPG.js.map +1 -0
- package/dist/{chunk-UPW4CMOH.js → chunk-NTQHMXWE.js} +122 -46
- package/dist/chunk-NTQHMXWE.js.map +1 -0
- package/dist/cli.cjs +351 -121
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +2 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +235 -81
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +267 -93
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +276 -102
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-GGZHR7VM.js → otel-grpc-QTX2YQJZ.js} +3 -3
- package/dist/server.cjs +120 -44
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-4V23KYOP.js.map +0 -1
- package/dist/chunk-N6RPINEJ.js.map +0 -1
- package/dist/chunk-UPW4CMOH.js.map +0 -1
- /package/dist/{chunk-IXIFJKMM.js.map → chunk-D5PIJFBE.js.map} +0 -0
- /package/dist/{otel-grpc-GGZHR7VM.js.map → otel-grpc-QTX2YQJZ.js.map} +0 -0
package/dist/cli.d.cts
CHANGED
|
@@ -40,6 +40,7 @@ interface ParsedArgs {
|
|
|
40
40
|
}
|
|
41
41
|
declare function parseArgs(rest: string[]): ParsedArgs;
|
|
42
42
|
|
|
43
|
+
declare function readPackageVersion(): string;
|
|
43
44
|
declare function runInit(opts: InitOptions): Promise<InitResult>;
|
|
44
45
|
declare const CLAUDE_SKILL_CONFIG: {
|
|
45
46
|
mcpServers: {
|
|
@@ -63,4 +64,4 @@ declare function runSkill(opts: SkillOptions): Promise<{
|
|
|
63
64
|
declare const QUERY_VERBS: Set<string>;
|
|
64
65
|
declare function runQueryVerb(cmd: string, parsed: ParsedArgs): Promise<number>;
|
|
65
66
|
|
|
66
|
-
export { CLAUDE_SKILL_CONFIG, type InitOptions, type InitResult, QUERY_VERBS, type SkillOptions, parseArgs, runInit, runQueryVerb, runSkill };
|
|
67
|
+
export { CLAUDE_SKILL_CONFIG, type InitOptions, type InitResult, QUERY_VERBS, type SkillOptions, parseArgs, readPackageVersion, runInit, runQueryVerb, runSkill };
|
package/dist/cli.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ interface ParsedArgs {
|
|
|
40
40
|
}
|
|
41
41
|
declare function parseArgs(rest: string[]): ParsedArgs;
|
|
42
42
|
|
|
43
|
+
declare function readPackageVersion(): string;
|
|
43
44
|
declare function runInit(opts: InitOptions): Promise<InitResult>;
|
|
44
45
|
declare const CLAUDE_SKILL_CONFIG: {
|
|
45
46
|
mcpServers: {
|
|
@@ -63,4 +64,4 @@ declare function runSkill(opts: SkillOptions): Promise<{
|
|
|
63
64
|
declare const QUERY_VERBS: Set<string>;
|
|
64
65
|
declare function runQueryVerb(cmd: string, parsed: ParsedArgs): Promise<number>;
|
|
65
66
|
|
|
66
|
-
export { CLAUDE_SKILL_CONFIG, type InitOptions, type InitResult, QUERY_VERBS, type SkillOptions, parseArgs, runInit, runQueryVerb, runSkill };
|
|
67
|
+
export { CLAUDE_SKILL_CONFIG, type InitOptions, type InitResult, QUERY_VERBS, type SkillOptions, parseArgs, readPackageVersion, runInit, runQueryVerb, runSkill };
|
package/dist/cli.js
CHANGED
|
@@ -41,18 +41,20 @@ import {
|
|
|
41
41
|
setStatus,
|
|
42
42
|
startPersistLoop,
|
|
43
43
|
startStalenessLoop
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-NTQHMXWE.js";
|
|
45
45
|
import {
|
|
46
46
|
startOtelGrpcReceiver
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-D5PIJFBE.js";
|
|
48
48
|
import {
|
|
49
|
+
__dirname,
|
|
49
50
|
__require,
|
|
50
51
|
buildOtelReceiver
|
|
51
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-KYRIQIPG.js";
|
|
52
53
|
|
|
53
54
|
// src/cli.ts
|
|
54
55
|
import path8 from "path";
|
|
55
|
-
import { promises as fs7 } from "fs";
|
|
56
|
+
import { promises as fs7, readFileSync } from "fs";
|
|
57
|
+
import { fileURLToPath } from "url";
|
|
56
58
|
|
|
57
59
|
// src/gitignore.ts
|
|
58
60
|
import { promises as fs } from "fs";
|
|
@@ -272,6 +274,14 @@ var IGNORED_WATCH_GLOBS = [
|
|
|
272
274
|
"**/.turbo/**",
|
|
273
275
|
"**/.next/**",
|
|
274
276
|
"**/neat-out/**",
|
|
277
|
+
// Python venv shapes (issue #344). chokidar opens one watch handle per
|
|
278
|
+
// descended dir; a CPython venv carries 20k+ files and trivially blows
|
|
279
|
+
// through the macOS kqueue cap before extraction even runs.
|
|
280
|
+
"**/.venv/**",
|
|
281
|
+
"**/venv/**",
|
|
282
|
+
"**/__pypackages__/**",
|
|
283
|
+
"**/.tox/**",
|
|
284
|
+
"**/site-packages/**",
|
|
275
285
|
"**/.DS_Store"
|
|
276
286
|
];
|
|
277
287
|
var IGNORED_WATCH_PATHS = [
|
|
@@ -282,6 +292,11 @@ var IGNORED_WATCH_PATHS = [
|
|
|
282
292
|
/(?:^|[\\/])\.turbo[\\/]/,
|
|
283
293
|
/(?:^|[\\/])\.next[\\/]/,
|
|
284
294
|
/(?:^|[\\/])neat-out[\\/]/,
|
|
295
|
+
/(?:^|[\\/])\.venv[\\/]/,
|
|
296
|
+
/(?:^|[\\/])venv[\\/]/,
|
|
297
|
+
/(?:^|[\\/])__pypackages__[\\/]/,
|
|
298
|
+
/(?:^|[\\/])\.tox[\\/]/,
|
|
299
|
+
/(?:^|[\\/])site-packages[\\/]/,
|
|
285
300
|
/[\\/]?\.DS_Store$/
|
|
286
301
|
];
|
|
287
302
|
function shouldIgnore(absPath) {
|
|
@@ -713,10 +728,10 @@ dotenv.config({ path: path.join(here, '.env.neat') })
|
|
|
713
728
|
|
|
714
729
|
await import('@opentelemetry/auto-instrumentations-node/register')
|
|
715
730
|
`;
|
|
716
|
-
function renderEnvNeat(
|
|
731
|
+
function renderEnvNeat(projectName) {
|
|
717
732
|
return [
|
|
718
733
|
"# Generated by `neat init --apply` (ADR-069).",
|
|
719
|
-
`OTEL_SERVICE_NAME=${
|
|
734
|
+
`OTEL_SERVICE_NAME=${projectName}`,
|
|
720
735
|
"OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318",
|
|
721
736
|
""
|
|
722
737
|
].join("\n");
|
|
@@ -737,41 +752,26 @@ export async function register() {
|
|
|
737
752
|
}
|
|
738
753
|
`;
|
|
739
754
|
var NEXT_INSTRUMENTATION_NODE_TS = `${NEXT_INSTRUMENTATION_HEADER}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
import path from 'node:path'
|
|
744
|
-
import dotenv from 'dotenv'
|
|
755
|
+
process.env.OTEL_SERVICE_NAME ||= '__PROJECT_NAME__'
|
|
756
|
+
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ||= 'http://localhost:4318'
|
|
757
|
+
|
|
745
758
|
import { NodeSDK } from '@opentelemetry/sdk-node'
|
|
746
759
|
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
|
|
747
760
|
|
|
748
|
-
|
|
749
|
-
dotenv.config({ path: path.join(here, '.env.neat') })
|
|
750
|
-
|
|
751
|
-
const sdk = new NodeSDK({
|
|
752
|
-
serviceName: process.env.OTEL_SERVICE_NAME,
|
|
753
|
-
instrumentations: [getNodeAutoInstrumentations()],
|
|
754
|
-
})
|
|
755
|
-
sdk.start()
|
|
761
|
+
new NodeSDK({ instrumentations: [getNodeAutoInstrumentations()] }).start()
|
|
756
762
|
`;
|
|
757
763
|
var NEXT_INSTRUMENTATION_NODE_JS = `${NEXT_INSTRUMENTATION_HEADER}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
import { fileURLToPath } from 'node:url'
|
|
761
|
-
import path from 'node:path'
|
|
762
|
-
import dotenv from 'dotenv'
|
|
763
|
-
import { NodeSDK } from '@opentelemetry/sdk-node'
|
|
764
|
-
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
|
|
764
|
+
process.env.OTEL_SERVICE_NAME ||= '__PROJECT_NAME__'
|
|
765
|
+
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ||= 'http://localhost:4318'
|
|
765
766
|
|
|
766
|
-
const
|
|
767
|
-
|
|
767
|
+
const { NodeSDK } = require('@opentelemetry/sdk-node')
|
|
768
|
+
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node')
|
|
768
769
|
|
|
769
|
-
|
|
770
|
-
serviceName: process.env.OTEL_SERVICE_NAME,
|
|
771
|
-
instrumentations: [getNodeAutoInstrumentations()],
|
|
772
|
-
})
|
|
773
|
-
sdk.start()
|
|
770
|
+
new NodeSDK({ instrumentations: [getNodeAutoInstrumentations()] }).start()
|
|
774
771
|
`;
|
|
772
|
+
function renderNextInstrumentationNode(template, projectName) {
|
|
773
|
+
return template.replace(/__PROJECT_NAME__/g, projectName);
|
|
774
|
+
}
|
|
775
775
|
var FRAMEWORK_OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-074). OpenTelemetry SDK hook.";
|
|
776
776
|
var FRAMEWORK_OTEL_INIT_TS_BODY = `${FRAMEWORK_OTEL_INIT_HEADER}
|
|
777
777
|
// Loads .env.neat (OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT) before
|
|
@@ -886,6 +886,10 @@ var OTEL_ENV = {
|
|
|
886
886
|
key: "OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
887
887
|
value: "http://localhost:4318"
|
|
888
888
|
};
|
|
889
|
+
function envServiceName(pkg, serviceDir, project) {
|
|
890
|
+
if (project && project.length > 0) return project;
|
|
891
|
+
return pkg.name ?? path4.basename(serviceDir);
|
|
892
|
+
}
|
|
889
893
|
async function readPackageJson(serviceDir) {
|
|
890
894
|
try {
|
|
891
895
|
const raw = await fs4.readFile(path4.join(serviceDir, "package.json"), "utf8");
|
|
@@ -1110,17 +1114,29 @@ function lineIsOtelInjection(line) {
|
|
|
1110
1114
|
if (trimmed.length === 0) return false;
|
|
1111
1115
|
return /(?:require\(|import\s+)['"]\.\/otel-init[^'"]*['"]/.test(trimmed);
|
|
1112
1116
|
}
|
|
1113
|
-
async function
|
|
1117
|
+
async function detectsSrcLayout(serviceDir) {
|
|
1118
|
+
const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
|
|
1119
|
+
exists(path4.join(serviceDir, "src", "app")),
|
|
1120
|
+
exists(path4.join(serviceDir, "src", "pages")),
|
|
1121
|
+
exists(path4.join(serviceDir, "app")),
|
|
1122
|
+
exists(path4.join(serviceDir, "pages"))
|
|
1123
|
+
]);
|
|
1124
|
+
return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
|
|
1125
|
+
}
|
|
1126
|
+
async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
|
|
1114
1127
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
1115
|
-
const
|
|
1128
|
+
const srcLayout = await detectsSrcLayout(serviceDir);
|
|
1129
|
+
const baseDir = srcLayout ? path4.join(serviceDir, "src") : serviceDir;
|
|
1130
|
+
const instrumentationFile = path4.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
|
|
1116
1131
|
const instrumentationNodeFile = path4.join(
|
|
1117
|
-
|
|
1132
|
+
baseDir,
|
|
1118
1133
|
useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
|
|
1119
1134
|
);
|
|
1120
|
-
const envNeatFile = path4.join(
|
|
1135
|
+
const envNeatFile = path4.join(baseDir, ".env.neat");
|
|
1121
1136
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
1122
1137
|
const dependencyEdits = [];
|
|
1123
1138
|
for (const sdk of SDK_PACKAGES) {
|
|
1139
|
+
if (sdk.name === "dotenv") continue;
|
|
1124
1140
|
if (sdk.name in existingDeps) continue;
|
|
1125
1141
|
dependencyEdits.push({
|
|
1126
1142
|
file: manifestPath,
|
|
@@ -1129,6 +1145,7 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath) {
|
|
|
1129
1145
|
version: sdk.version
|
|
1130
1146
|
});
|
|
1131
1147
|
}
|
|
1148
|
+
const projectName = envServiceName(pkg, serviceDir, project);
|
|
1132
1149
|
const generatedFiles = [];
|
|
1133
1150
|
if (!await exists(instrumentationFile)) {
|
|
1134
1151
|
generatedFiles.push({
|
|
@@ -1140,14 +1157,17 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath) {
|
|
|
1140
1157
|
if (!await exists(instrumentationNodeFile)) {
|
|
1141
1158
|
generatedFiles.push({
|
|
1142
1159
|
file: instrumentationNodeFile,
|
|
1143
|
-
contents:
|
|
1160
|
+
contents: renderNextInstrumentationNode(
|
|
1161
|
+
useTs ? NEXT_INSTRUMENTATION_NODE_TS : NEXT_INSTRUMENTATION_NODE_JS,
|
|
1162
|
+
projectName
|
|
1163
|
+
),
|
|
1144
1164
|
skipIfExists: true
|
|
1145
1165
|
});
|
|
1146
1166
|
}
|
|
1147
1167
|
if (!await exists(envNeatFile)) {
|
|
1148
1168
|
generatedFiles.push({
|
|
1149
1169
|
file: envNeatFile,
|
|
1150
|
-
contents: renderEnvNeat(
|
|
1170
|
+
contents: renderEnvNeat(projectName),
|
|
1151
1171
|
skipIfExists: true
|
|
1152
1172
|
});
|
|
1153
1173
|
}
|
|
@@ -1203,12 +1223,12 @@ function buildDependencyEdits(pkg, manifestPath) {
|
|
|
1203
1223
|
}
|
|
1204
1224
|
return edits;
|
|
1205
1225
|
}
|
|
1206
|
-
async function queueEnvNeat(serviceDir, pkg, generatedFiles) {
|
|
1226
|
+
async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
|
|
1207
1227
|
const envNeatFile = path4.join(serviceDir, ".env.neat");
|
|
1208
1228
|
if (!await exists(envNeatFile)) {
|
|
1209
1229
|
generatedFiles.push({
|
|
1210
1230
|
file: envNeatFile,
|
|
1211
|
-
contents: renderEnvNeat(pkg
|
|
1231
|
+
contents: renderEnvNeat(envServiceName(pkg, serviceDir, project)),
|
|
1212
1232
|
skipIfExists: true
|
|
1213
1233
|
});
|
|
1214
1234
|
}
|
|
@@ -1227,7 +1247,7 @@ function fileImportsOtelHook(raw, specifiers) {
|
|
|
1227
1247
|
}
|
|
1228
1248
|
return false;
|
|
1229
1249
|
}
|
|
1230
|
-
async function planRemix(serviceDir, pkg, manifestPath, entryFile) {
|
|
1250
|
+
async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
1231
1251
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
1232
1252
|
const otelServerFile = path4.join(
|
|
1233
1253
|
serviceDir,
|
|
@@ -1242,7 +1262,7 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile) {
|
|
|
1242
1262
|
skipIfExists: true
|
|
1243
1263
|
});
|
|
1244
1264
|
}
|
|
1245
|
-
await queueEnvNeat(serviceDir, pkg, generatedFiles);
|
|
1265
|
+
await queueEnvNeat(serviceDir, pkg, project, generatedFiles);
|
|
1246
1266
|
const entrypointEdits = [];
|
|
1247
1267
|
try {
|
|
1248
1268
|
const raw = await fs4.readFile(entryFile, "utf8");
|
|
@@ -1279,7 +1299,7 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile) {
|
|
|
1279
1299
|
framework: "remix"
|
|
1280
1300
|
};
|
|
1281
1301
|
}
|
|
1282
|
-
async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile) {
|
|
1302
|
+
async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
|
|
1283
1303
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
1284
1304
|
const otelInitFile = path4.join(
|
|
1285
1305
|
serviceDir,
|
|
@@ -1296,7 +1316,7 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile) {
|
|
|
1296
1316
|
skipIfExists: true
|
|
1297
1317
|
});
|
|
1298
1318
|
}
|
|
1299
|
-
await queueEnvNeat(serviceDir, pkg, generatedFiles);
|
|
1319
|
+
await queueEnvNeat(serviceDir, pkg, project, generatedFiles);
|
|
1300
1320
|
if (hooksFile === null) {
|
|
1301
1321
|
generatedFiles.push({
|
|
1302
1322
|
file: resolvedHooksFile,
|
|
@@ -1340,7 +1360,7 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile) {
|
|
|
1340
1360
|
framework: "sveltekit"
|
|
1341
1361
|
};
|
|
1342
1362
|
}
|
|
1343
|
-
async function planNuxt(serviceDir, pkg, manifestPath) {
|
|
1363
|
+
async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
1344
1364
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
1345
1365
|
const otelPluginFile = path4.join(
|
|
1346
1366
|
serviceDir,
|
|
@@ -1366,7 +1386,7 @@ async function planNuxt(serviceDir, pkg, manifestPath) {
|
|
|
1366
1386
|
skipIfExists: true
|
|
1367
1387
|
});
|
|
1368
1388
|
}
|
|
1369
|
-
await queueEnvNeat(serviceDir, pkg, generatedFiles);
|
|
1389
|
+
await queueEnvNeat(serviceDir, pkg, project, generatedFiles);
|
|
1370
1390
|
const empty = dependencyEdits.length === 0 && generatedFiles.length === 0;
|
|
1371
1391
|
if (empty) {
|
|
1372
1392
|
return {
|
|
@@ -1397,7 +1417,7 @@ async function findAstroMiddleware(serviceDir) {
|
|
|
1397
1417
|
}
|
|
1398
1418
|
return null;
|
|
1399
1419
|
}
|
|
1400
|
-
async function planAstro(serviceDir, pkg, manifestPath) {
|
|
1420
|
+
async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
1401
1421
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
1402
1422
|
const otelInitFile = path4.join(
|
|
1403
1423
|
serviceDir,
|
|
@@ -1415,7 +1435,7 @@ async function planAstro(serviceDir, pkg, manifestPath) {
|
|
|
1415
1435
|
skipIfExists: true
|
|
1416
1436
|
});
|
|
1417
1437
|
}
|
|
1418
|
-
await queueEnvNeat(serviceDir, pkg, generatedFiles);
|
|
1438
|
+
await queueEnvNeat(serviceDir, pkg, project, generatedFiles);
|
|
1419
1439
|
if (existingMiddleware === null) {
|
|
1420
1440
|
generatedFiles.push({
|
|
1421
1441
|
file: middlewareFile,
|
|
@@ -1459,9 +1479,10 @@ async function planAstro(serviceDir, pkg, manifestPath) {
|
|
|
1459
1479
|
framework: "astro"
|
|
1460
1480
|
};
|
|
1461
1481
|
}
|
|
1462
|
-
async function plan(serviceDir) {
|
|
1482
|
+
async function plan(serviceDir, opts) {
|
|
1463
1483
|
const pkg = await readPackageJson(serviceDir);
|
|
1464
1484
|
const manifestPath = path4.join(serviceDir, "package.json");
|
|
1485
|
+
const project = opts?.project;
|
|
1465
1486
|
const empty = {
|
|
1466
1487
|
language: "javascript",
|
|
1467
1488
|
serviceDir,
|
|
@@ -1474,32 +1495,32 @@ async function plan(serviceDir) {
|
|
|
1474
1495
|
if (hasNextDependency(pkg)) {
|
|
1475
1496
|
const nextConfig = await findNextConfig(serviceDir);
|
|
1476
1497
|
if (nextConfig) {
|
|
1477
|
-
return planNext(serviceDir, pkg, manifestPath, nextConfig);
|
|
1498
|
+
return planNext(serviceDir, pkg, manifestPath, nextConfig, project);
|
|
1478
1499
|
}
|
|
1479
1500
|
}
|
|
1480
1501
|
if (hasRemixDependency(pkg)) {
|
|
1481
1502
|
const remixEntry = await findRemixEntry(serviceDir);
|
|
1482
1503
|
if (remixEntry) {
|
|
1483
|
-
return planRemix(serviceDir, pkg, manifestPath, remixEntry);
|
|
1504
|
+
return planRemix(serviceDir, pkg, manifestPath, remixEntry, project);
|
|
1484
1505
|
}
|
|
1485
1506
|
}
|
|
1486
1507
|
if (hasSvelteKitDependency(pkg)) {
|
|
1487
1508
|
const hooks = await findSvelteKitHooks(serviceDir);
|
|
1488
1509
|
const config = await findSvelteKitConfig(serviceDir);
|
|
1489
1510
|
if (hooks || config) {
|
|
1490
|
-
return planSvelteKit(serviceDir, pkg, manifestPath, hooks);
|
|
1511
|
+
return planSvelteKit(serviceDir, pkg, manifestPath, hooks, project);
|
|
1491
1512
|
}
|
|
1492
1513
|
}
|
|
1493
1514
|
if (hasNuxtDependency(pkg)) {
|
|
1494
1515
|
const nuxtConfig = await findNuxtConfig(serviceDir);
|
|
1495
1516
|
if (nuxtConfig) {
|
|
1496
|
-
return planNuxt(serviceDir, pkg, manifestPath);
|
|
1517
|
+
return planNuxt(serviceDir, pkg, manifestPath, project);
|
|
1497
1518
|
}
|
|
1498
1519
|
}
|
|
1499
1520
|
if (hasAstroDependency(pkg)) {
|
|
1500
1521
|
const astroConfig = await findAstroConfig(serviceDir);
|
|
1501
1522
|
if (astroConfig) {
|
|
1502
|
-
return planAstro(serviceDir, pkg, manifestPath);
|
|
1523
|
+
return planAstro(serviceDir, pkg, manifestPath, project);
|
|
1503
1524
|
}
|
|
1504
1525
|
}
|
|
1505
1526
|
const entryFile = await resolveEntry(serviceDir, pkg);
|
|
@@ -1547,7 +1568,7 @@ async function plan(serviceDir) {
|
|
|
1547
1568
|
if (!await exists(envNeatFile)) {
|
|
1548
1569
|
generatedFiles.push({
|
|
1549
1570
|
file: envNeatFile,
|
|
1550
|
-
contents: renderEnvNeat(pkg
|
|
1571
|
+
contents: renderEnvNeat(envServiceName(pkg, serviceDir, project)),
|
|
1551
1572
|
skipIfExists: true
|
|
1552
1573
|
});
|
|
1553
1574
|
}
|
|
@@ -1572,9 +1593,13 @@ function isAllowedWritePath(serviceDir, target) {
|
|
|
1572
1593
|
if (base === "package.json") return true;
|
|
1573
1594
|
if (base === ".env.neat") return true;
|
|
1574
1595
|
if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
1575
|
-
if (/^instrumentation(?:\.node)?\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
1576
|
-
if (/^next\.config\.(?:js|mjs|ts)$/.test(base)) return true;
|
|
1577
1596
|
const relPosix = rel.split(path4.sep).join("/");
|
|
1597
|
+
if (/^instrumentation(?:\.node)?\.(?:js|cjs|mjs|ts)$/.test(base)) {
|
|
1598
|
+
if (relPosix === base) return true;
|
|
1599
|
+
if (relPosix === `src/${base}`) return true;
|
|
1600
|
+
return false;
|
|
1601
|
+
}
|
|
1602
|
+
if (/^next\.config\.(?:js|mjs|ts)$/.test(base)) return true;
|
|
1578
1603
|
if (relPosix === "app/otel.server.ts" || relPosix === "app/otel.server.js") return true;
|
|
1579
1604
|
if (/^app\/entry\.server\.(?:tsx?|jsx?)$/.test(relPosix)) return true;
|
|
1580
1605
|
if (relPosix === "src/otel-init.ts" || relPosix === "src/otel-init.js") return true;
|
|
@@ -2079,14 +2104,14 @@ async function extractAndPersist(opts) {
|
|
|
2079
2104
|
errorsPath: projectPaths.errorsPath
|
|
2080
2105
|
};
|
|
2081
2106
|
}
|
|
2082
|
-
async function applyInstallersOver(services) {
|
|
2107
|
+
async function applyInstallersOver(services, project) {
|
|
2083
2108
|
let instrumented = 0;
|
|
2084
2109
|
let already = 0;
|
|
2085
2110
|
let libOnly = 0;
|
|
2086
2111
|
for (const svc of services) {
|
|
2087
2112
|
const installer = await pickInstaller(svc.dir);
|
|
2088
2113
|
if (!installer) continue;
|
|
2089
|
-
const plan3 = await installer.plan(svc.dir);
|
|
2114
|
+
const plan3 = await installer.plan(svc.dir, { project });
|
|
2090
2115
|
if (isEmptyPlan(plan3) && !plan3.libOnly) {
|
|
2091
2116
|
already++;
|
|
2092
2117
|
continue;
|
|
@@ -2108,28 +2133,106 @@ async function promptYesNo(question) {
|
|
|
2108
2133
|
});
|
|
2109
2134
|
});
|
|
2110
2135
|
}
|
|
2111
|
-
var DEFAULT_DAEMON_READY_TIMEOUT_MS =
|
|
2112
|
-
|
|
2136
|
+
var DEFAULT_DAEMON_READY_TIMEOUT_MS = 6e4;
|
|
2137
|
+
var PROBE_INTERVAL_MS = 500;
|
|
2138
|
+
async function fetchDaemonHealth(restPort) {
|
|
2113
2139
|
return new Promise((resolve) => {
|
|
2114
2140
|
const req = http.get(`http://127.0.0.1:${restPort}/health`, (res) => {
|
|
2115
2141
|
const ok = res.statusCode !== void 0 && res.statusCode >= 200 && res.statusCode < 300;
|
|
2116
|
-
|
|
2117
|
-
|
|
2142
|
+
if (!ok) {
|
|
2143
|
+
res.resume();
|
|
2144
|
+
resolve(null);
|
|
2145
|
+
return;
|
|
2146
|
+
}
|
|
2147
|
+
let body = "";
|
|
2148
|
+
res.setEncoding("utf8");
|
|
2149
|
+
res.on("data", (chunk) => {
|
|
2150
|
+
body += chunk;
|
|
2151
|
+
});
|
|
2152
|
+
res.on("end", () => {
|
|
2153
|
+
try {
|
|
2154
|
+
resolve(JSON.parse(body));
|
|
2155
|
+
} catch {
|
|
2156
|
+
resolve({ ok: true });
|
|
2157
|
+
}
|
|
2158
|
+
});
|
|
2118
2159
|
});
|
|
2119
|
-
req.on("error", () => resolve(
|
|
2160
|
+
req.on("error", () => resolve(null));
|
|
2120
2161
|
req.setTimeout(1e3, () => {
|
|
2121
2162
|
req.destroy();
|
|
2122
|
-
resolve(
|
|
2163
|
+
resolve(null);
|
|
2123
2164
|
});
|
|
2124
2165
|
});
|
|
2125
2166
|
}
|
|
2167
|
+
async function checkDaemonHealth(restPort) {
|
|
2168
|
+
const body = await fetchDaemonHealth(restPort);
|
|
2169
|
+
return body !== null;
|
|
2170
|
+
}
|
|
2171
|
+
async function probeProjectHealth(restPort, name) {
|
|
2172
|
+
return new Promise((resolve) => {
|
|
2173
|
+
const req = http.get(
|
|
2174
|
+
`http://127.0.0.1:${restPort}/projects/${encodeURIComponent(name)}/health`,
|
|
2175
|
+
(res) => {
|
|
2176
|
+
const code = res.statusCode ?? 0;
|
|
2177
|
+
res.resume();
|
|
2178
|
+
if (code >= 200 && code < 300) resolve("active");
|
|
2179
|
+
else resolve("bootstrapping");
|
|
2180
|
+
}
|
|
2181
|
+
);
|
|
2182
|
+
req.on("error", () => resolve("bootstrapping"));
|
|
2183
|
+
req.setTimeout(1e3, () => {
|
|
2184
|
+
req.destroy();
|
|
2185
|
+
resolve("bootstrapping");
|
|
2186
|
+
});
|
|
2187
|
+
});
|
|
2188
|
+
}
|
|
2189
|
+
async function snapshotProjectStatus(restPort, body) {
|
|
2190
|
+
if (body.projects && body.projects.length > 0) {
|
|
2191
|
+
return body.projects.map((p) => ({
|
|
2192
|
+
name: p.name,
|
|
2193
|
+
status: p.status ?? "active"
|
|
2194
|
+
}));
|
|
2195
|
+
}
|
|
2196
|
+
const entries = await listProjects().catch(() => []);
|
|
2197
|
+
if (entries.length === 0) return [];
|
|
2198
|
+
return Promise.all(
|
|
2199
|
+
entries.map(async (entry2) => ({
|
|
2200
|
+
name: entry2.name,
|
|
2201
|
+
status: await probeProjectHealth(restPort, entry2.name)
|
|
2202
|
+
}))
|
|
2203
|
+
);
|
|
2204
|
+
}
|
|
2126
2205
|
async function waitForDaemonReady(restPort, timeoutMs) {
|
|
2127
2206
|
const deadline = Date.now() + timeoutMs;
|
|
2207
|
+
let lastBootstrapping = [];
|
|
2128
2208
|
while (Date.now() < deadline) {
|
|
2129
|
-
|
|
2130
|
-
|
|
2209
|
+
const body = await fetchDaemonHealth(restPort);
|
|
2210
|
+
if (body !== null) {
|
|
2211
|
+
const projects2 = await snapshotProjectStatus(restPort, body);
|
|
2212
|
+
const bootstrapping = projects2.filter((p) => p.status === "bootstrapping").map((p) => p.name);
|
|
2213
|
+
const broken = projects2.filter((p) => p.status === "broken").map((p) => p.name);
|
|
2214
|
+
if (bootstrapping.length === 0) {
|
|
2215
|
+
return { ready: true, brokenProjects: broken, stillBootstrapping: [] };
|
|
2216
|
+
}
|
|
2217
|
+
const key = bootstrapping.slice().sort().join(",");
|
|
2218
|
+
const prevKey = lastBootstrapping.slice().sort().join(",");
|
|
2219
|
+
if (key !== prevKey) {
|
|
2220
|
+
const plural = bootstrapping.length === 1 ? "" : "s";
|
|
2221
|
+
console.log(
|
|
2222
|
+
`neat: waiting on ${bootstrapping.length} project${plural}: ${bootstrapping.join(", ")}`
|
|
2223
|
+
);
|
|
2224
|
+
lastBootstrapping = bootstrapping;
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
await new Promise((r) => setTimeout(r, PROBE_INTERVAL_MS));
|
|
2131
2228
|
}
|
|
2132
|
-
|
|
2229
|
+
const final = await fetchDaemonHealth(restPort);
|
|
2230
|
+
const projects = final ? await snapshotProjectStatus(restPort, final) : [];
|
|
2231
|
+
return {
|
|
2232
|
+
ready: false,
|
|
2233
|
+
brokenProjects: projects.filter((p) => p.status === "broken").map((p) => p.name),
|
|
2234
|
+
stillBootstrapping: projects.filter((p) => p.status === "bootstrapping").map((p) => p.name)
|
|
2235
|
+
};
|
|
2133
2236
|
}
|
|
2134
2237
|
function spawnDaemonDetached() {
|
|
2135
2238
|
const here = path6.dirname(new URL(import.meta.url).pathname);
|
|
@@ -2150,12 +2253,21 @@ function spawnDaemonDetached() {
|
|
|
2150
2253
|
if (!entry2) {
|
|
2151
2254
|
throw new Error(`orchestrator: cannot locate neatd entry in ${here}`);
|
|
2152
2255
|
}
|
|
2256
|
+
const env = { ...process.env };
|
|
2257
|
+
const hasToken = typeof env.NEAT_AUTH_TOKEN === "string" && env.NEAT_AUTH_TOKEN.length > 0;
|
|
2258
|
+
if (!hasToken && (!env.HOST || env.HOST.length === 0)) {
|
|
2259
|
+
env.HOST = "127.0.0.1";
|
|
2260
|
+
}
|
|
2153
2261
|
const child = spawn2(process.execPath, [entry2, "start"], {
|
|
2154
2262
|
detached: true,
|
|
2155
|
-
|
|
2156
|
-
|
|
2263
|
+
// stderr inherits the orchestrator's fd so the daemon's
|
|
2264
|
+
// `BindAuthorityError` message lands in front of the operator instead
|
|
2265
|
+
// of being swallowed (issue #341).
|
|
2266
|
+
stdio: ["ignore", "ignore", "inherit"],
|
|
2267
|
+
env
|
|
2157
2268
|
});
|
|
2158
2269
|
child.unref();
|
|
2270
|
+
return child;
|
|
2159
2271
|
}
|
|
2160
2272
|
function openBrowser(url) {
|
|
2161
2273
|
if (!process.stdout.isTTY) return "failed";
|
|
@@ -2231,7 +2343,7 @@ async function runOrchestrator(opts) {
|
|
|
2231
2343
|
result.steps.apply.skipped = true;
|
|
2232
2344
|
console.log("skipped instrumentation (--no-instrument)");
|
|
2233
2345
|
} else {
|
|
2234
|
-
const tally = await applyInstallersOver(services);
|
|
2346
|
+
const tally = await applyInstallersOver(services, opts.project);
|
|
2235
2347
|
result.steps.apply = { ...tally, skipped: false };
|
|
2236
2348
|
console.log(
|
|
2237
2349
|
`instrumented ${tally.instrumented}, already ${tally.alreadyInstrumented}, lib-only ${tally.libOnly}`
|
|
@@ -2250,12 +2362,25 @@ async function runOrchestrator(opts) {
|
|
|
2250
2362
|
return result;
|
|
2251
2363
|
}
|
|
2252
2364
|
const ready = await waitForDaemonReady(restPort, timeoutMs);
|
|
2253
|
-
result.steps.daemon = ready ? "spawned" : "timed-out";
|
|
2254
|
-
if (!ready) {
|
|
2365
|
+
result.steps.daemon = ready.ready ? "spawned" : "timed-out";
|
|
2366
|
+
if (!ready.ready) {
|
|
2255
2367
|
console.error(`neat: daemon did not become ready within ${timeoutMs}ms`);
|
|
2368
|
+
if (ready.stillBootstrapping.length > 0) {
|
|
2369
|
+
console.error(
|
|
2370
|
+
`neat: still bootstrapping: ${ready.stillBootstrapping.join(", ")}`
|
|
2371
|
+
);
|
|
2372
|
+
}
|
|
2373
|
+
if (ready.brokenProjects.length > 0) {
|
|
2374
|
+
console.error(`neat: broken projects: ${ready.brokenProjects.join(", ")}`);
|
|
2375
|
+
}
|
|
2256
2376
|
result.exitCode = 1;
|
|
2257
2377
|
return result;
|
|
2258
2378
|
}
|
|
2379
|
+
if (ready.brokenProjects.length > 0) {
|
|
2380
|
+
console.warn(
|
|
2381
|
+
`neat: ${ready.brokenProjects.length} project(s) reported broken: ${ready.brokenProjects.join(", ")}`
|
|
2382
|
+
);
|
|
2383
|
+
}
|
|
2259
2384
|
}
|
|
2260
2385
|
const dashboardUrl = opts.dashboardUrl ?? "http://localhost:6328";
|
|
2261
2386
|
if (opts.noOpen || !process.stdout.isTTY) {
|
|
@@ -2884,7 +3009,7 @@ async function runSync(opts) {
|
|
|
2884
3009
|
snapshotPath = target;
|
|
2885
3010
|
}
|
|
2886
3011
|
const skipApply = opts.dryRun || opts.noInstrument;
|
|
2887
|
-
const applyTally = skipApply ? { instrumented: 0, alreadyInstrumented: 0, libOnly: 0 } : await applyInstallersOver(persisted.services);
|
|
3012
|
+
const applyTally = skipApply ? { instrumented: 0, alreadyInstrumented: 0, libOnly: 0 } : await applyInstallersOver(persisted.services, entry2.name);
|
|
2888
3013
|
const warnings = [];
|
|
2889
3014
|
let daemonState = "skipped";
|
|
2890
3015
|
let exitCode = 0;
|
|
@@ -2980,6 +3105,8 @@ function usage() {
|
|
|
2980
3105
|
console.log(" uninstall <name>");
|
|
2981
3106
|
console.log(" Remove a project from the registry. Does not touch");
|
|
2982
3107
|
console.log(" neat-out/, policy.json, or any user file.");
|
|
3108
|
+
console.log(" version Print the installed @neat.is/core version and exit.");
|
|
3109
|
+
console.log(" Aliases: --version, -v.");
|
|
2983
3110
|
console.log(" skill Install or print the Claude Code MCP drop-in.");
|
|
2984
3111
|
console.log(" Flags:");
|
|
2985
3112
|
console.log(" --print-config print the JSON snippet to stdout");
|
|
@@ -3106,7 +3233,7 @@ function parseArgs(rest) {
|
|
|
3106
3233
|
out.yes = true;
|
|
3107
3234
|
continue;
|
|
3108
3235
|
}
|
|
3109
|
-
if (arg === "--verbose"
|
|
3236
|
+
if (arg === "--verbose") {
|
|
3110
3237
|
out.verbose = true;
|
|
3111
3238
|
continue;
|
|
3112
3239
|
}
|
|
@@ -3165,6 +3292,28 @@ function assignFlag(out, field, value) {
|
|
|
3165
3292
|
;
|
|
3166
3293
|
out[field] = value;
|
|
3167
3294
|
}
|
|
3295
|
+
function readPackageVersion() {
|
|
3296
|
+
const here = typeof __dirname !== "undefined" ? __dirname : path8.dirname(fileURLToPath(import.meta.url));
|
|
3297
|
+
const candidates = [
|
|
3298
|
+
path8.resolve(here, "../package.json"),
|
|
3299
|
+
path8.resolve(here, "../../package.json")
|
|
3300
|
+
];
|
|
3301
|
+
for (const candidate of candidates) {
|
|
3302
|
+
try {
|
|
3303
|
+
const raw = readFileSync(candidate, "utf8");
|
|
3304
|
+
const parsed = JSON.parse(raw);
|
|
3305
|
+
if (parsed.name === "@neat.is/core" && typeof parsed.version === "string") {
|
|
3306
|
+
return parsed.version;
|
|
3307
|
+
}
|
|
3308
|
+
} catch {
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
return "unknown";
|
|
3312
|
+
}
|
|
3313
|
+
function printVersion() {
|
|
3314
|
+
process.stdout.write(`${readPackageVersion()}
|
|
3315
|
+
`);
|
|
3316
|
+
}
|
|
3168
3317
|
function printBanner() {
|
|
3169
3318
|
console.log("\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557");
|
|
3170
3319
|
console.log("\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D");
|
|
@@ -3174,7 +3323,7 @@ function printBanner() {
|
|
|
3174
3323
|
console.log("\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D ");
|
|
3175
3324
|
console.log("");
|
|
3176
3325
|
console.log(" Network Expressive Architecting Tool");
|
|
3177
|
-
console.log(" neat.is \xB7 v0.
|
|
3326
|
+
console.log(" neat.is \xB7 v0.4.0 \xB7 Apache 2.0");
|
|
3178
3327
|
console.log("");
|
|
3179
3328
|
}
|
|
3180
3329
|
function printDiscoveryReport(opts, services) {
|
|
@@ -3202,12 +3351,12 @@ function printDiscoveryReport(opts, services) {
|
|
|
3202
3351
|
}
|
|
3203
3352
|
console.log("");
|
|
3204
3353
|
}
|
|
3205
|
-
async function buildPatchSections(services) {
|
|
3354
|
+
async function buildPatchSections(services, project) {
|
|
3206
3355
|
const sections = [];
|
|
3207
3356
|
for (const svc of services) {
|
|
3208
3357
|
const installer = await pickInstaller(svc.dir);
|
|
3209
3358
|
if (!installer) continue;
|
|
3210
|
-
const plan3 = await installer.plan(svc.dir);
|
|
3359
|
+
const plan3 = await installer.plan(svc.dir, { project });
|
|
3211
3360
|
if (isEmptyPlan(plan3) && !plan3.libOnly) continue;
|
|
3212
3361
|
sections.push({ installer: installer.name, plan: plan3 });
|
|
3213
3362
|
}
|
|
@@ -3222,7 +3371,7 @@ async function runInit(opts) {
|
|
|
3222
3371
|
}
|
|
3223
3372
|
const services = await discoverServices(opts.scanPath);
|
|
3224
3373
|
printDiscoveryReport(opts, services);
|
|
3225
|
-
const sections = opts.noInstall ? [] : await buildPatchSections(services);
|
|
3374
|
+
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
3226
3375
|
const patch = renderPatch(sections);
|
|
3227
3376
|
const patchPath = path8.join(opts.scanPath, "neat.patch");
|
|
3228
3377
|
if (opts.dryRun) {
|
|
@@ -3380,6 +3529,10 @@ async function main() {
|
|
|
3380
3529
|
usage();
|
|
3381
3530
|
process.exit(0);
|
|
3382
3531
|
}
|
|
3532
|
+
if (cmd === "--version" || cmd === "-v" || cmd === "version") {
|
|
3533
|
+
printVersion();
|
|
3534
|
+
process.exit(0);
|
|
3535
|
+
}
|
|
3383
3536
|
const parsed = parseArgs(rest);
|
|
3384
3537
|
const { positional, apply: apply3, dryRun, noInstall } = parsed;
|
|
3385
3538
|
const project = parsed.project ?? DEFAULT_PROJECT;
|
|
@@ -3775,7 +3928,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
3775
3928
|
}
|
|
3776
3929
|
}
|
|
3777
3930
|
var entry = process.argv[1] ?? "";
|
|
3778
|
-
if (/[\\/]cli\.(?:cjs|js)$/.test(entry) || entry.endsWith("/cli") || entry.endsWith("/neat")) {
|
|
3931
|
+
if (/[\\/]cli\.(?:cjs|js)$/.test(entry) || entry.endsWith("/cli") || entry.endsWith("/neat") || entry.endsWith("/neat.is")) {
|
|
3779
3932
|
main().catch((err) => {
|
|
3780
3933
|
console.error(err);
|
|
3781
3934
|
process.exit(1);
|
|
@@ -3785,6 +3938,7 @@ export {
|
|
|
3785
3938
|
CLAUDE_SKILL_CONFIG,
|
|
3786
3939
|
QUERY_VERBS,
|
|
3787
3940
|
parseArgs,
|
|
3941
|
+
readPackageVersion,
|
|
3788
3942
|
runInit,
|
|
3789
3943
|
runQueryVerb,
|
|
3790
3944
|
runSkill
|