@neat.is/core 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-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 +312 -87
- 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 +196 -47
- 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");
|
|
@@ -886,6 +901,10 @@ var OTEL_ENV = {
|
|
|
886
901
|
key: "OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
887
902
|
value: "http://localhost:4318"
|
|
888
903
|
};
|
|
904
|
+
function envServiceName(pkg, serviceDir, project) {
|
|
905
|
+
if (project && project.length > 0) return project;
|
|
906
|
+
return pkg.name ?? path4.basename(serviceDir);
|
|
907
|
+
}
|
|
889
908
|
async function readPackageJson(serviceDir) {
|
|
890
909
|
try {
|
|
891
910
|
const raw = await fs4.readFile(path4.join(serviceDir, "package.json"), "utf8");
|
|
@@ -1110,7 +1129,7 @@ function lineIsOtelInjection(line) {
|
|
|
1110
1129
|
if (trimmed.length === 0) return false;
|
|
1111
1130
|
return /(?:require\(|import\s+)['"]\.\/otel-init[^'"]*['"]/.test(trimmed);
|
|
1112
1131
|
}
|
|
1113
|
-
async function planNext(serviceDir, pkg, manifestPath, nextConfigPath) {
|
|
1132
|
+
async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
|
|
1114
1133
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
1115
1134
|
const instrumentationFile = path4.join(serviceDir, useTs ? "instrumentation.ts" : "instrumentation.js");
|
|
1116
1135
|
const instrumentationNodeFile = path4.join(
|
|
@@ -1147,7 +1166,7 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath) {
|
|
|
1147
1166
|
if (!await exists(envNeatFile)) {
|
|
1148
1167
|
generatedFiles.push({
|
|
1149
1168
|
file: envNeatFile,
|
|
1150
|
-
contents: renderEnvNeat(pkg
|
|
1169
|
+
contents: renderEnvNeat(envServiceName(pkg, serviceDir, project)),
|
|
1151
1170
|
skipIfExists: true
|
|
1152
1171
|
});
|
|
1153
1172
|
}
|
|
@@ -1203,12 +1222,12 @@ function buildDependencyEdits(pkg, manifestPath) {
|
|
|
1203
1222
|
}
|
|
1204
1223
|
return edits;
|
|
1205
1224
|
}
|
|
1206
|
-
async function queueEnvNeat(serviceDir, pkg, generatedFiles) {
|
|
1225
|
+
async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
|
|
1207
1226
|
const envNeatFile = path4.join(serviceDir, ".env.neat");
|
|
1208
1227
|
if (!await exists(envNeatFile)) {
|
|
1209
1228
|
generatedFiles.push({
|
|
1210
1229
|
file: envNeatFile,
|
|
1211
|
-
contents: renderEnvNeat(pkg
|
|
1230
|
+
contents: renderEnvNeat(envServiceName(pkg, serviceDir, project)),
|
|
1212
1231
|
skipIfExists: true
|
|
1213
1232
|
});
|
|
1214
1233
|
}
|
|
@@ -1227,7 +1246,7 @@ function fileImportsOtelHook(raw, specifiers) {
|
|
|
1227
1246
|
}
|
|
1228
1247
|
return false;
|
|
1229
1248
|
}
|
|
1230
|
-
async function planRemix(serviceDir, pkg, manifestPath, entryFile) {
|
|
1249
|
+
async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
1231
1250
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
1232
1251
|
const otelServerFile = path4.join(
|
|
1233
1252
|
serviceDir,
|
|
@@ -1242,7 +1261,7 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile) {
|
|
|
1242
1261
|
skipIfExists: true
|
|
1243
1262
|
});
|
|
1244
1263
|
}
|
|
1245
|
-
await queueEnvNeat(serviceDir, pkg, generatedFiles);
|
|
1264
|
+
await queueEnvNeat(serviceDir, pkg, project, generatedFiles);
|
|
1246
1265
|
const entrypointEdits = [];
|
|
1247
1266
|
try {
|
|
1248
1267
|
const raw = await fs4.readFile(entryFile, "utf8");
|
|
@@ -1279,7 +1298,7 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile) {
|
|
|
1279
1298
|
framework: "remix"
|
|
1280
1299
|
};
|
|
1281
1300
|
}
|
|
1282
|
-
async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile) {
|
|
1301
|
+
async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
|
|
1283
1302
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
1284
1303
|
const otelInitFile = path4.join(
|
|
1285
1304
|
serviceDir,
|
|
@@ -1296,7 +1315,7 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile) {
|
|
|
1296
1315
|
skipIfExists: true
|
|
1297
1316
|
});
|
|
1298
1317
|
}
|
|
1299
|
-
await queueEnvNeat(serviceDir, pkg, generatedFiles);
|
|
1318
|
+
await queueEnvNeat(serviceDir, pkg, project, generatedFiles);
|
|
1300
1319
|
if (hooksFile === null) {
|
|
1301
1320
|
generatedFiles.push({
|
|
1302
1321
|
file: resolvedHooksFile,
|
|
@@ -1340,7 +1359,7 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile) {
|
|
|
1340
1359
|
framework: "sveltekit"
|
|
1341
1360
|
};
|
|
1342
1361
|
}
|
|
1343
|
-
async function planNuxt(serviceDir, pkg, manifestPath) {
|
|
1362
|
+
async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
1344
1363
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
1345
1364
|
const otelPluginFile = path4.join(
|
|
1346
1365
|
serviceDir,
|
|
@@ -1366,7 +1385,7 @@ async function planNuxt(serviceDir, pkg, manifestPath) {
|
|
|
1366
1385
|
skipIfExists: true
|
|
1367
1386
|
});
|
|
1368
1387
|
}
|
|
1369
|
-
await queueEnvNeat(serviceDir, pkg, generatedFiles);
|
|
1388
|
+
await queueEnvNeat(serviceDir, pkg, project, generatedFiles);
|
|
1370
1389
|
const empty = dependencyEdits.length === 0 && generatedFiles.length === 0;
|
|
1371
1390
|
if (empty) {
|
|
1372
1391
|
return {
|
|
@@ -1397,7 +1416,7 @@ async function findAstroMiddleware(serviceDir) {
|
|
|
1397
1416
|
}
|
|
1398
1417
|
return null;
|
|
1399
1418
|
}
|
|
1400
|
-
async function planAstro(serviceDir, pkg, manifestPath) {
|
|
1419
|
+
async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
1401
1420
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
1402
1421
|
const otelInitFile = path4.join(
|
|
1403
1422
|
serviceDir,
|
|
@@ -1415,7 +1434,7 @@ async function planAstro(serviceDir, pkg, manifestPath) {
|
|
|
1415
1434
|
skipIfExists: true
|
|
1416
1435
|
});
|
|
1417
1436
|
}
|
|
1418
|
-
await queueEnvNeat(serviceDir, pkg, generatedFiles);
|
|
1437
|
+
await queueEnvNeat(serviceDir, pkg, project, generatedFiles);
|
|
1419
1438
|
if (existingMiddleware === null) {
|
|
1420
1439
|
generatedFiles.push({
|
|
1421
1440
|
file: middlewareFile,
|
|
@@ -1459,9 +1478,10 @@ async function planAstro(serviceDir, pkg, manifestPath) {
|
|
|
1459
1478
|
framework: "astro"
|
|
1460
1479
|
};
|
|
1461
1480
|
}
|
|
1462
|
-
async function plan(serviceDir) {
|
|
1481
|
+
async function plan(serviceDir, opts) {
|
|
1463
1482
|
const pkg = await readPackageJson(serviceDir);
|
|
1464
1483
|
const manifestPath = path4.join(serviceDir, "package.json");
|
|
1484
|
+
const project = opts?.project;
|
|
1465
1485
|
const empty = {
|
|
1466
1486
|
language: "javascript",
|
|
1467
1487
|
serviceDir,
|
|
@@ -1474,32 +1494,32 @@ async function plan(serviceDir) {
|
|
|
1474
1494
|
if (hasNextDependency(pkg)) {
|
|
1475
1495
|
const nextConfig = await findNextConfig(serviceDir);
|
|
1476
1496
|
if (nextConfig) {
|
|
1477
|
-
return planNext(serviceDir, pkg, manifestPath, nextConfig);
|
|
1497
|
+
return planNext(serviceDir, pkg, manifestPath, nextConfig, project);
|
|
1478
1498
|
}
|
|
1479
1499
|
}
|
|
1480
1500
|
if (hasRemixDependency(pkg)) {
|
|
1481
1501
|
const remixEntry = await findRemixEntry(serviceDir);
|
|
1482
1502
|
if (remixEntry) {
|
|
1483
|
-
return planRemix(serviceDir, pkg, manifestPath, remixEntry);
|
|
1503
|
+
return planRemix(serviceDir, pkg, manifestPath, remixEntry, project);
|
|
1484
1504
|
}
|
|
1485
1505
|
}
|
|
1486
1506
|
if (hasSvelteKitDependency(pkg)) {
|
|
1487
1507
|
const hooks = await findSvelteKitHooks(serviceDir);
|
|
1488
1508
|
const config = await findSvelteKitConfig(serviceDir);
|
|
1489
1509
|
if (hooks || config) {
|
|
1490
|
-
return planSvelteKit(serviceDir, pkg, manifestPath, hooks);
|
|
1510
|
+
return planSvelteKit(serviceDir, pkg, manifestPath, hooks, project);
|
|
1491
1511
|
}
|
|
1492
1512
|
}
|
|
1493
1513
|
if (hasNuxtDependency(pkg)) {
|
|
1494
1514
|
const nuxtConfig = await findNuxtConfig(serviceDir);
|
|
1495
1515
|
if (nuxtConfig) {
|
|
1496
|
-
return planNuxt(serviceDir, pkg, manifestPath);
|
|
1516
|
+
return planNuxt(serviceDir, pkg, manifestPath, project);
|
|
1497
1517
|
}
|
|
1498
1518
|
}
|
|
1499
1519
|
if (hasAstroDependency(pkg)) {
|
|
1500
1520
|
const astroConfig = await findAstroConfig(serviceDir);
|
|
1501
1521
|
if (astroConfig) {
|
|
1502
|
-
return planAstro(serviceDir, pkg, manifestPath);
|
|
1522
|
+
return planAstro(serviceDir, pkg, manifestPath, project);
|
|
1503
1523
|
}
|
|
1504
1524
|
}
|
|
1505
1525
|
const entryFile = await resolveEntry(serviceDir, pkg);
|
|
@@ -1547,7 +1567,7 @@ async function plan(serviceDir) {
|
|
|
1547
1567
|
if (!await exists(envNeatFile)) {
|
|
1548
1568
|
generatedFiles.push({
|
|
1549
1569
|
file: envNeatFile,
|
|
1550
|
-
contents: renderEnvNeat(pkg
|
|
1570
|
+
contents: renderEnvNeat(envServiceName(pkg, serviceDir, project)),
|
|
1551
1571
|
skipIfExists: true
|
|
1552
1572
|
});
|
|
1553
1573
|
}
|
|
@@ -2079,14 +2099,14 @@ async function extractAndPersist(opts) {
|
|
|
2079
2099
|
errorsPath: projectPaths.errorsPath
|
|
2080
2100
|
};
|
|
2081
2101
|
}
|
|
2082
|
-
async function applyInstallersOver(services) {
|
|
2102
|
+
async function applyInstallersOver(services, project) {
|
|
2083
2103
|
let instrumented = 0;
|
|
2084
2104
|
let already = 0;
|
|
2085
2105
|
let libOnly = 0;
|
|
2086
2106
|
for (const svc of services) {
|
|
2087
2107
|
const installer = await pickInstaller(svc.dir);
|
|
2088
2108
|
if (!installer) continue;
|
|
2089
|
-
const plan3 = await installer.plan(svc.dir);
|
|
2109
|
+
const plan3 = await installer.plan(svc.dir, { project });
|
|
2090
2110
|
if (isEmptyPlan(plan3) && !plan3.libOnly) {
|
|
2091
2111
|
already++;
|
|
2092
2112
|
continue;
|
|
@@ -2108,28 +2128,106 @@ async function promptYesNo(question) {
|
|
|
2108
2128
|
});
|
|
2109
2129
|
});
|
|
2110
2130
|
}
|
|
2111
|
-
var DEFAULT_DAEMON_READY_TIMEOUT_MS =
|
|
2112
|
-
|
|
2131
|
+
var DEFAULT_DAEMON_READY_TIMEOUT_MS = 6e4;
|
|
2132
|
+
var PROBE_INTERVAL_MS = 500;
|
|
2133
|
+
async function fetchDaemonHealth(restPort) {
|
|
2113
2134
|
return new Promise((resolve) => {
|
|
2114
2135
|
const req = http.get(`http://127.0.0.1:${restPort}/health`, (res) => {
|
|
2115
2136
|
const ok = res.statusCode !== void 0 && res.statusCode >= 200 && res.statusCode < 300;
|
|
2116
|
-
|
|
2117
|
-
|
|
2137
|
+
if (!ok) {
|
|
2138
|
+
res.resume();
|
|
2139
|
+
resolve(null);
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
let body = "";
|
|
2143
|
+
res.setEncoding("utf8");
|
|
2144
|
+
res.on("data", (chunk) => {
|
|
2145
|
+
body += chunk;
|
|
2146
|
+
});
|
|
2147
|
+
res.on("end", () => {
|
|
2148
|
+
try {
|
|
2149
|
+
resolve(JSON.parse(body));
|
|
2150
|
+
} catch {
|
|
2151
|
+
resolve({ ok: true });
|
|
2152
|
+
}
|
|
2153
|
+
});
|
|
2118
2154
|
});
|
|
2119
|
-
req.on("error", () => resolve(
|
|
2155
|
+
req.on("error", () => resolve(null));
|
|
2120
2156
|
req.setTimeout(1e3, () => {
|
|
2121
2157
|
req.destroy();
|
|
2122
|
-
resolve(
|
|
2158
|
+
resolve(null);
|
|
2123
2159
|
});
|
|
2124
2160
|
});
|
|
2125
2161
|
}
|
|
2162
|
+
async function checkDaemonHealth(restPort) {
|
|
2163
|
+
const body = await fetchDaemonHealth(restPort);
|
|
2164
|
+
return body !== null;
|
|
2165
|
+
}
|
|
2166
|
+
async function probeProjectHealth(restPort, name) {
|
|
2167
|
+
return new Promise((resolve) => {
|
|
2168
|
+
const req = http.get(
|
|
2169
|
+
`http://127.0.0.1:${restPort}/projects/${encodeURIComponent(name)}/health`,
|
|
2170
|
+
(res) => {
|
|
2171
|
+
const code = res.statusCode ?? 0;
|
|
2172
|
+
res.resume();
|
|
2173
|
+
if (code >= 200 && code < 300) resolve("active");
|
|
2174
|
+
else resolve("bootstrapping");
|
|
2175
|
+
}
|
|
2176
|
+
);
|
|
2177
|
+
req.on("error", () => resolve("bootstrapping"));
|
|
2178
|
+
req.setTimeout(1e3, () => {
|
|
2179
|
+
req.destroy();
|
|
2180
|
+
resolve("bootstrapping");
|
|
2181
|
+
});
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
async function snapshotProjectStatus(restPort, body) {
|
|
2185
|
+
if (body.projects && body.projects.length > 0) {
|
|
2186
|
+
return body.projects.map((p) => ({
|
|
2187
|
+
name: p.name,
|
|
2188
|
+
status: p.status ?? "active"
|
|
2189
|
+
}));
|
|
2190
|
+
}
|
|
2191
|
+
const entries = await listProjects().catch(() => []);
|
|
2192
|
+
if (entries.length === 0) return [];
|
|
2193
|
+
return Promise.all(
|
|
2194
|
+
entries.map(async (entry2) => ({
|
|
2195
|
+
name: entry2.name,
|
|
2196
|
+
status: await probeProjectHealth(restPort, entry2.name)
|
|
2197
|
+
}))
|
|
2198
|
+
);
|
|
2199
|
+
}
|
|
2126
2200
|
async function waitForDaemonReady(restPort, timeoutMs) {
|
|
2127
2201
|
const deadline = Date.now() + timeoutMs;
|
|
2202
|
+
let lastBootstrapping = [];
|
|
2128
2203
|
while (Date.now() < deadline) {
|
|
2129
|
-
|
|
2130
|
-
|
|
2204
|
+
const body = await fetchDaemonHealth(restPort);
|
|
2205
|
+
if (body !== null) {
|
|
2206
|
+
const projects2 = await snapshotProjectStatus(restPort, body);
|
|
2207
|
+
const bootstrapping = projects2.filter((p) => p.status === "bootstrapping").map((p) => p.name);
|
|
2208
|
+
const broken = projects2.filter((p) => p.status === "broken").map((p) => p.name);
|
|
2209
|
+
if (bootstrapping.length === 0) {
|
|
2210
|
+
return { ready: true, brokenProjects: broken, stillBootstrapping: [] };
|
|
2211
|
+
}
|
|
2212
|
+
const key = bootstrapping.slice().sort().join(",");
|
|
2213
|
+
const prevKey = lastBootstrapping.slice().sort().join(",");
|
|
2214
|
+
if (key !== prevKey) {
|
|
2215
|
+
const plural = bootstrapping.length === 1 ? "" : "s";
|
|
2216
|
+
console.log(
|
|
2217
|
+
`neat: waiting on ${bootstrapping.length} project${plural}: ${bootstrapping.join(", ")}`
|
|
2218
|
+
);
|
|
2219
|
+
lastBootstrapping = bootstrapping;
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
await new Promise((r) => setTimeout(r, PROBE_INTERVAL_MS));
|
|
2131
2223
|
}
|
|
2132
|
-
|
|
2224
|
+
const final = await fetchDaemonHealth(restPort);
|
|
2225
|
+
const projects = final ? await snapshotProjectStatus(restPort, final) : [];
|
|
2226
|
+
return {
|
|
2227
|
+
ready: false,
|
|
2228
|
+
brokenProjects: projects.filter((p) => p.status === "broken").map((p) => p.name),
|
|
2229
|
+
stillBootstrapping: projects.filter((p) => p.status === "bootstrapping").map((p) => p.name)
|
|
2230
|
+
};
|
|
2133
2231
|
}
|
|
2134
2232
|
function spawnDaemonDetached() {
|
|
2135
2233
|
const here = path6.dirname(new URL(import.meta.url).pathname);
|
|
@@ -2150,12 +2248,21 @@ function spawnDaemonDetached() {
|
|
|
2150
2248
|
if (!entry2) {
|
|
2151
2249
|
throw new Error(`orchestrator: cannot locate neatd entry in ${here}`);
|
|
2152
2250
|
}
|
|
2251
|
+
const env = { ...process.env };
|
|
2252
|
+
const hasToken = typeof env.NEAT_AUTH_TOKEN === "string" && env.NEAT_AUTH_TOKEN.length > 0;
|
|
2253
|
+
if (!hasToken && (!env.HOST || env.HOST.length === 0)) {
|
|
2254
|
+
env.HOST = "127.0.0.1";
|
|
2255
|
+
}
|
|
2153
2256
|
const child = spawn2(process.execPath, [entry2, "start"], {
|
|
2154
2257
|
detached: true,
|
|
2155
|
-
|
|
2156
|
-
|
|
2258
|
+
// stderr inherits the orchestrator's fd so the daemon's
|
|
2259
|
+
// `BindAuthorityError` message lands in front of the operator instead
|
|
2260
|
+
// of being swallowed (issue #341).
|
|
2261
|
+
stdio: ["ignore", "ignore", "inherit"],
|
|
2262
|
+
env
|
|
2157
2263
|
});
|
|
2158
2264
|
child.unref();
|
|
2265
|
+
return child;
|
|
2159
2266
|
}
|
|
2160
2267
|
function openBrowser(url) {
|
|
2161
2268
|
if (!process.stdout.isTTY) return "failed";
|
|
@@ -2231,7 +2338,7 @@ async function runOrchestrator(opts) {
|
|
|
2231
2338
|
result.steps.apply.skipped = true;
|
|
2232
2339
|
console.log("skipped instrumentation (--no-instrument)");
|
|
2233
2340
|
} else {
|
|
2234
|
-
const tally = await applyInstallersOver(services);
|
|
2341
|
+
const tally = await applyInstallersOver(services, opts.project);
|
|
2235
2342
|
result.steps.apply = { ...tally, skipped: false };
|
|
2236
2343
|
console.log(
|
|
2237
2344
|
`instrumented ${tally.instrumented}, already ${tally.alreadyInstrumented}, lib-only ${tally.libOnly}`
|
|
@@ -2250,12 +2357,25 @@ async function runOrchestrator(opts) {
|
|
|
2250
2357
|
return result;
|
|
2251
2358
|
}
|
|
2252
2359
|
const ready = await waitForDaemonReady(restPort, timeoutMs);
|
|
2253
|
-
result.steps.daemon = ready ? "spawned" : "timed-out";
|
|
2254
|
-
if (!ready) {
|
|
2360
|
+
result.steps.daemon = ready.ready ? "spawned" : "timed-out";
|
|
2361
|
+
if (!ready.ready) {
|
|
2255
2362
|
console.error(`neat: daemon did not become ready within ${timeoutMs}ms`);
|
|
2363
|
+
if (ready.stillBootstrapping.length > 0) {
|
|
2364
|
+
console.error(
|
|
2365
|
+
`neat: still bootstrapping: ${ready.stillBootstrapping.join(", ")}`
|
|
2366
|
+
);
|
|
2367
|
+
}
|
|
2368
|
+
if (ready.brokenProjects.length > 0) {
|
|
2369
|
+
console.error(`neat: broken projects: ${ready.brokenProjects.join(", ")}`);
|
|
2370
|
+
}
|
|
2256
2371
|
result.exitCode = 1;
|
|
2257
2372
|
return result;
|
|
2258
2373
|
}
|
|
2374
|
+
if (ready.brokenProjects.length > 0) {
|
|
2375
|
+
console.warn(
|
|
2376
|
+
`neat: ${ready.brokenProjects.length} project(s) reported broken: ${ready.brokenProjects.join(", ")}`
|
|
2377
|
+
);
|
|
2378
|
+
}
|
|
2259
2379
|
}
|
|
2260
2380
|
const dashboardUrl = opts.dashboardUrl ?? "http://localhost:6328";
|
|
2261
2381
|
if (opts.noOpen || !process.stdout.isTTY) {
|
|
@@ -2884,7 +3004,7 @@ async function runSync(opts) {
|
|
|
2884
3004
|
snapshotPath = target;
|
|
2885
3005
|
}
|
|
2886
3006
|
const skipApply = opts.dryRun || opts.noInstrument;
|
|
2887
|
-
const applyTally = skipApply ? { instrumented: 0, alreadyInstrumented: 0, libOnly: 0 } : await applyInstallersOver(persisted.services);
|
|
3007
|
+
const applyTally = skipApply ? { instrumented: 0, alreadyInstrumented: 0, libOnly: 0 } : await applyInstallersOver(persisted.services, entry2.name);
|
|
2888
3008
|
const warnings = [];
|
|
2889
3009
|
let daemonState = "skipped";
|
|
2890
3010
|
let exitCode = 0;
|
|
@@ -2980,6 +3100,8 @@ function usage() {
|
|
|
2980
3100
|
console.log(" uninstall <name>");
|
|
2981
3101
|
console.log(" Remove a project from the registry. Does not touch");
|
|
2982
3102
|
console.log(" neat-out/, policy.json, or any user file.");
|
|
3103
|
+
console.log(" version Print the installed @neat.is/core version and exit.");
|
|
3104
|
+
console.log(" Aliases: --version, -v.");
|
|
2983
3105
|
console.log(" skill Install or print the Claude Code MCP drop-in.");
|
|
2984
3106
|
console.log(" Flags:");
|
|
2985
3107
|
console.log(" --print-config print the JSON snippet to stdout");
|
|
@@ -3106,7 +3228,7 @@ function parseArgs(rest) {
|
|
|
3106
3228
|
out.yes = true;
|
|
3107
3229
|
continue;
|
|
3108
3230
|
}
|
|
3109
|
-
if (arg === "--verbose"
|
|
3231
|
+
if (arg === "--verbose") {
|
|
3110
3232
|
out.verbose = true;
|
|
3111
3233
|
continue;
|
|
3112
3234
|
}
|
|
@@ -3165,6 +3287,28 @@ function assignFlag(out, field, value) {
|
|
|
3165
3287
|
;
|
|
3166
3288
|
out[field] = value;
|
|
3167
3289
|
}
|
|
3290
|
+
function readPackageVersion() {
|
|
3291
|
+
const here = typeof __dirname !== "undefined" ? __dirname : path8.dirname(fileURLToPath(import.meta.url));
|
|
3292
|
+
const candidates = [
|
|
3293
|
+
path8.resolve(here, "../package.json"),
|
|
3294
|
+
path8.resolve(here, "../../package.json")
|
|
3295
|
+
];
|
|
3296
|
+
for (const candidate of candidates) {
|
|
3297
|
+
try {
|
|
3298
|
+
const raw = readFileSync(candidate, "utf8");
|
|
3299
|
+
const parsed = JSON.parse(raw);
|
|
3300
|
+
if (parsed.name === "@neat.is/core" && typeof parsed.version === "string") {
|
|
3301
|
+
return parsed.version;
|
|
3302
|
+
}
|
|
3303
|
+
} catch {
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
return "unknown";
|
|
3307
|
+
}
|
|
3308
|
+
function printVersion() {
|
|
3309
|
+
process.stdout.write(`${readPackageVersion()}
|
|
3310
|
+
`);
|
|
3311
|
+
}
|
|
3168
3312
|
function printBanner() {
|
|
3169
3313
|
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
3314
|
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 +3318,7 @@ function printBanner() {
|
|
|
3174
3318
|
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
3319
|
console.log("");
|
|
3176
3320
|
console.log(" Network Expressive Architecting Tool");
|
|
3177
|
-
console.log(" neat.is \xB7 v0.
|
|
3321
|
+
console.log(" neat.is \xB7 v0.4.0 \xB7 Apache 2.0");
|
|
3178
3322
|
console.log("");
|
|
3179
3323
|
}
|
|
3180
3324
|
function printDiscoveryReport(opts, services) {
|
|
@@ -3202,12 +3346,12 @@ function printDiscoveryReport(opts, services) {
|
|
|
3202
3346
|
}
|
|
3203
3347
|
console.log("");
|
|
3204
3348
|
}
|
|
3205
|
-
async function buildPatchSections(services) {
|
|
3349
|
+
async function buildPatchSections(services, project) {
|
|
3206
3350
|
const sections = [];
|
|
3207
3351
|
for (const svc of services) {
|
|
3208
3352
|
const installer = await pickInstaller(svc.dir);
|
|
3209
3353
|
if (!installer) continue;
|
|
3210
|
-
const plan3 = await installer.plan(svc.dir);
|
|
3354
|
+
const plan3 = await installer.plan(svc.dir, { project });
|
|
3211
3355
|
if (isEmptyPlan(plan3) && !plan3.libOnly) continue;
|
|
3212
3356
|
sections.push({ installer: installer.name, plan: plan3 });
|
|
3213
3357
|
}
|
|
@@ -3222,7 +3366,7 @@ async function runInit(opts) {
|
|
|
3222
3366
|
}
|
|
3223
3367
|
const services = await discoverServices(opts.scanPath);
|
|
3224
3368
|
printDiscoveryReport(opts, services);
|
|
3225
|
-
const sections = opts.noInstall ? [] : await buildPatchSections(services);
|
|
3369
|
+
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
3226
3370
|
const patch = renderPatch(sections);
|
|
3227
3371
|
const patchPath = path8.join(opts.scanPath, "neat.patch");
|
|
3228
3372
|
if (opts.dryRun) {
|
|
@@ -3380,6 +3524,10 @@ async function main() {
|
|
|
3380
3524
|
usage();
|
|
3381
3525
|
process.exit(0);
|
|
3382
3526
|
}
|
|
3527
|
+
if (cmd === "--version" || cmd === "-v" || cmd === "version") {
|
|
3528
|
+
printVersion();
|
|
3529
|
+
process.exit(0);
|
|
3530
|
+
}
|
|
3383
3531
|
const parsed = parseArgs(rest);
|
|
3384
3532
|
const { positional, apply: apply3, dryRun, noInstall } = parsed;
|
|
3385
3533
|
const project = parsed.project ?? DEFAULT_PROJECT;
|
|
@@ -3785,6 +3933,7 @@ export {
|
|
|
3785
3933
|
CLAUDE_SKILL_CONFIG,
|
|
3786
3934
|
QUERY_VERBS,
|
|
3787
3935
|
parseArgs,
|
|
3936
|
+
readPackageVersion,
|
|
3788
3937
|
runInit,
|
|
3789
3938
|
runQueryVerb,
|
|
3790
3939
|
runSkill
|