@neat.is/core 0.6.1 → 0.6.2-dev.20260723
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-BZ3AJVAC.js → chunk-2MAGU6RB.js} +11 -2
- package/dist/chunk-2MAGU6RB.js.map +1 -0
- package/dist/{chunk-3YUNROLT.js → chunk-64JJOXES.js} +3 -3
- package/dist/{chunk-MVINCLQM.js → chunk-OY7FGJAX.js} +2 -2
- package/dist/{chunk-7C6ZULNU.js → chunk-ZZ3VUCWL.js} +1492 -1346
- package/dist/chunk-ZZ3VUCWL.js.map +1 -0
- package/dist/cli.cjs +783 -468
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +170 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +466 -310
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +475 -319
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-XS45HBET.js → otel-grpc-AHNPM5LX.js} +3 -3
- package/dist/server.cjs +2400 -2240
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-7C6ZULNU.js.map +0 -1
- package/dist/chunk-BZ3AJVAC.js.map +0 -1
- /package/dist/{chunk-3YUNROLT.js.map → chunk-64JJOXES.js.map} +0 -0
- /package/dist/{chunk-MVINCLQM.js.map → chunk-OY7FGJAX.js.map} +0 -0
- /package/dist/{otel-grpc-XS45HBET.js.map → otel-grpc-AHNPM5LX.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
resolveNeatVersion,
|
|
13
13
|
validateConnectorEntry,
|
|
14
14
|
writeDaemonRecord
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-64JJOXES.js";
|
|
16
16
|
import {
|
|
17
17
|
buildSearchIndex
|
|
18
18
|
} from "./chunk-BIY46Q6U.js";
|
|
@@ -71,10 +71,10 @@ import {
|
|
|
71
71
|
startPersistLoop,
|
|
72
72
|
startStalenessLoop,
|
|
73
73
|
upsertConnectorEntry
|
|
74
|
-
} from "./chunk-
|
|
74
|
+
} from "./chunk-ZZ3VUCWL.js";
|
|
75
75
|
import {
|
|
76
76
|
startOtelGrpcReceiver
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-OY7FGJAX.js";
|
|
78
78
|
import {
|
|
79
79
|
__dirname,
|
|
80
80
|
__require,
|
|
@@ -82,7 +82,7 @@ import {
|
|
|
82
82
|
buildOtelReceiver,
|
|
83
83
|
listenSteppingOtlp,
|
|
84
84
|
readAuthEnv
|
|
85
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-2MAGU6RB.js";
|
|
86
86
|
|
|
87
87
|
// src/cli.ts
|
|
88
88
|
import path10 from "path";
|
|
@@ -2609,6 +2609,132 @@ var OTEL_ENV2 = {
|
|
|
2609
2609
|
key: "OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
2610
2610
|
value: "http://localhost:4318"
|
|
2611
2611
|
};
|
|
2612
|
+
var NEAT_OTEL_FILENAME = "neat_otel.py";
|
|
2613
|
+
var NEAT_OTEL_STAMP = "neat-otel-init v1";
|
|
2614
|
+
var NEAT_IMPORT_LINE = "import neat_otel # neat: call-site file attribution";
|
|
2615
|
+
function neatOtelPy() {
|
|
2616
|
+
return `# ${NEAT_OTEL_STAMP} \u2014 generated by NEAT. Safe to re-generate; do not edit.
|
|
2617
|
+
# Call-site file attribution (docs/contracts/file-awareness.md section 4). Stamps
|
|
2618
|
+
# code.file.path / code.line.number / code.function.name on spans so NEAT fuses
|
|
2619
|
+
# runtime spans onto your source files. Import it once at your entry point.
|
|
2620
|
+
import os
|
|
2621
|
+
import sys
|
|
2622
|
+
|
|
2623
|
+
try:
|
|
2624
|
+
from opentelemetry import trace as _neat_trace
|
|
2625
|
+
from opentelemetry.sdk.trace import SpanProcessor as _NeatSpanProcessor
|
|
2626
|
+
from opentelemetry.trace import SpanKind as _NeatSpanKind
|
|
2627
|
+
|
|
2628
|
+
_NEAT_OTEL = True
|
|
2629
|
+
except Exception:
|
|
2630
|
+
_NEAT_OTEL = False
|
|
2631
|
+
|
|
2632
|
+
if _NEAT_OTEL:
|
|
2633
|
+
_NEAT_SELF = os.path.abspath(__file__)
|
|
2634
|
+
_NEAT_PREFIXES = tuple(
|
|
2635
|
+
os.path.abspath(p) + os.sep
|
|
2636
|
+
for p in {sys.prefix, sys.base_prefix, sys.exec_prefix, sys.base_exec_prefix}
|
|
2637
|
+
)
|
|
2638
|
+
_NEAT_KINDS = {_NeatSpanKind.CLIENT, _NeatSpanKind.PRODUCER, _NeatSpanKind.SERVER}
|
|
2639
|
+
|
|
2640
|
+
def _neat_is_user_frame(filename):
|
|
2641
|
+
if not filename:
|
|
2642
|
+
return False
|
|
2643
|
+
if filename.startswith("<") and filename.endswith(">"):
|
|
2644
|
+
return False
|
|
2645
|
+
abs_name = os.path.abspath(filename)
|
|
2646
|
+
if abs_name == _NEAT_SELF:
|
|
2647
|
+
return False
|
|
2648
|
+
parts = abs_name.split(os.sep)
|
|
2649
|
+
if "opentelemetry" in parts or "site-packages" in parts:
|
|
2650
|
+
return False
|
|
2651
|
+
for pref in _NEAT_PREFIXES:
|
|
2652
|
+
if abs_name.startswith(pref):
|
|
2653
|
+
return False
|
|
2654
|
+
return True
|
|
2655
|
+
|
|
2656
|
+
class NeatCallSiteSpanProcessor(_NeatSpanProcessor):
|
|
2657
|
+
def on_start(self, span, parent_context=None):
|
|
2658
|
+
try:
|
|
2659
|
+
if span.kind not in _NEAT_KINDS:
|
|
2660
|
+
return
|
|
2661
|
+
frame = sys._getframe(1)
|
|
2662
|
+
while frame is not None:
|
|
2663
|
+
if _neat_is_user_frame(frame.f_code.co_filename):
|
|
2664
|
+
span.set_attribute("code.file.path", os.path.abspath(frame.f_code.co_filename))
|
|
2665
|
+
span.set_attribute("code.line.number", frame.f_lineno)
|
|
2666
|
+
span.set_attribute("code.function.name", frame.f_code.co_name)
|
|
2667
|
+
return
|
|
2668
|
+
frame = frame.f_back
|
|
2669
|
+
except Exception:
|
|
2670
|
+
pass # never break the host application
|
|
2671
|
+
|
|
2672
|
+
def on_end(self, span):
|
|
2673
|
+
pass
|
|
2674
|
+
|
|
2675
|
+
def shutdown(self):
|
|
2676
|
+
pass
|
|
2677
|
+
|
|
2678
|
+
def force_flush(self, timeout_millis=30000):
|
|
2679
|
+
return True
|
|
2680
|
+
|
|
2681
|
+
def _neat_register():
|
|
2682
|
+
try:
|
|
2683
|
+
provider = _neat_trace.get_tracer_provider()
|
|
2684
|
+
add = getattr(provider, "add_span_processor", None)
|
|
2685
|
+
if callable(add):
|
|
2686
|
+
add(NeatCallSiteSpanProcessor())
|
|
2687
|
+
except Exception:
|
|
2688
|
+
pass
|
|
2689
|
+
|
|
2690
|
+
if os.environ.get("NEAT_CALLSITE_DISABLED", "") != "1":
|
|
2691
|
+
_neat_register()
|
|
2692
|
+
`;
|
|
2693
|
+
}
|
|
2694
|
+
async function writeFileAtomic(file, contents) {
|
|
2695
|
+
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
2696
|
+
await fs5.writeFile(tmp, contents, "utf8");
|
|
2697
|
+
await fs5.rename(tmp, file);
|
|
2698
|
+
}
|
|
2699
|
+
async function resolvePyEntrypoint(serviceDir) {
|
|
2700
|
+
const procfile = path6.join(serviceDir, "Procfile");
|
|
2701
|
+
if (await exists2(procfile)) {
|
|
2702
|
+
const raw = await fs5.readFile(procfile, "utf8");
|
|
2703
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
2704
|
+
const m = line.match(/^[a-zA-Z0-9_-]+:\s*(.+)$/);
|
|
2705
|
+
if (!m) continue;
|
|
2706
|
+
const cmd = m[1];
|
|
2707
|
+
const asgi = cmd.match(/\b(?:uvicorn|gunicorn|hypercorn|daphne)\s+([\w.]+):/);
|
|
2708
|
+
if (asgi) {
|
|
2709
|
+
const modPath = asgi[1].replace(/\./g, "/");
|
|
2710
|
+
const asFile = path6.join(serviceDir, `${modPath}.py`);
|
|
2711
|
+
if (await exists2(asFile)) return asFile;
|
|
2712
|
+
const asPkg = path6.join(serviceDir, modPath, "__init__.py");
|
|
2713
|
+
if (await exists2(asPkg)) return asPkg;
|
|
2714
|
+
}
|
|
2715
|
+
const runFile = cmd.match(/\b(?:python3?|fastapi\s+(?:run|dev))\s+([\w./-]+\.py)\b/);
|
|
2716
|
+
if (runFile) {
|
|
2717
|
+
const p = path6.join(serviceDir, runFile[1]);
|
|
2718
|
+
if (await exists2(p)) return p;
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
for (const name of ["main.py", "app.py", "asgi.py", "wsgi.py", "manage.py", "server.py"]) {
|
|
2723
|
+
const p = path6.join(serviceDir, name);
|
|
2724
|
+
if (await exists2(p)) return p;
|
|
2725
|
+
}
|
|
2726
|
+
return null;
|
|
2727
|
+
}
|
|
2728
|
+
function injectNeatImport(source) {
|
|
2729
|
+
if (/^\s*import\s+neat_otel\b/m.test(source)) return null;
|
|
2730
|
+
const lines = source.split("\n");
|
|
2731
|
+
let insertAt = lines[0]?.startsWith("#!") ? 1 : 0;
|
|
2732
|
+
for (let i = 0; i < Math.min(lines.length, 15); i++) {
|
|
2733
|
+
if (/^\s*from\s+__future__\s+import\b/.test(lines[i] ?? "")) insertAt = i + 1;
|
|
2734
|
+
}
|
|
2735
|
+
lines.splice(insertAt, 0, NEAT_IMPORT_LINE);
|
|
2736
|
+
return lines.join("\n");
|
|
2737
|
+
}
|
|
2612
2738
|
async function exists2(p) {
|
|
2613
2739
|
try {
|
|
2614
2740
|
await fs5.stat(p);
|
|
@@ -2677,7 +2803,9 @@ async function plan2(serviceDir) {
|
|
|
2677
2803
|
}
|
|
2678
2804
|
}
|
|
2679
2805
|
const entrypointEdits = await planProcfileEdits(serviceDir);
|
|
2680
|
-
|
|
2806
|
+
const entryFile = await resolvePyEntrypoint(serviceDir);
|
|
2807
|
+
const generatedFiles = entryFile ? [{ file: path6.join(serviceDir, NEAT_OTEL_FILENAME), contents: neatOtelPy() }] : [];
|
|
2808
|
+
if (dependencyEdits.length === 0 && entrypointEdits.length === 0 && !entryFile) {
|
|
2681
2809
|
return empty;
|
|
2682
2810
|
}
|
|
2683
2811
|
return {
|
|
@@ -2685,7 +2813,9 @@ async function plan2(serviceDir) {
|
|
|
2685
2813
|
serviceDir,
|
|
2686
2814
|
dependencyEdits,
|
|
2687
2815
|
entrypointEdits,
|
|
2688
|
-
envEdits: [OTEL_ENV2]
|
|
2816
|
+
envEdits: [OTEL_ENV2],
|
|
2817
|
+
...generatedFiles.length > 0 ? { generatedFiles } : {},
|
|
2818
|
+
...entryFile ? { entryFile } : {}
|
|
2689
2819
|
};
|
|
2690
2820
|
}
|
|
2691
2821
|
async function applyRequirementsTxt(manifest, edits, original) {
|
|
@@ -2708,11 +2838,13 @@ async function applyProcfile(procfile, edits, original) {
|
|
|
2708
2838
|
await fs5.rename(tmp, procfile);
|
|
2709
2839
|
}
|
|
2710
2840
|
async function apply2(installPlan) {
|
|
2711
|
-
const { serviceDir } = installPlan;
|
|
2841
|
+
const { serviceDir, entryFile } = installPlan;
|
|
2842
|
+
const generatedFiles = installPlan.generatedFiles ?? [];
|
|
2712
2843
|
const touched = /* @__PURE__ */ new Set();
|
|
2713
2844
|
for (const e of installPlan.dependencyEdits) touched.add(e.file);
|
|
2714
2845
|
for (const e of installPlan.entrypointEdits) touched.add(e.file);
|
|
2715
|
-
if (touched.
|
|
2846
|
+
if (entryFile) touched.add(entryFile);
|
|
2847
|
+
if (touched.size === 0 && generatedFiles.length === 0) {
|
|
2716
2848
|
return { serviceDir, outcome: "already-instrumented", writtenFiles: [] };
|
|
2717
2849
|
}
|
|
2718
2850
|
const originals = /* @__PURE__ */ new Map();
|
|
@@ -2723,7 +2855,18 @@ async function apply2(installPlan) {
|
|
|
2723
2855
|
}
|
|
2724
2856
|
}
|
|
2725
2857
|
const writtenFiles = [];
|
|
2858
|
+
const createdFiles = [];
|
|
2726
2859
|
try {
|
|
2860
|
+
for (const gf of generatedFiles) {
|
|
2861
|
+
const already = await exists2(gf.file);
|
|
2862
|
+
if (already) {
|
|
2863
|
+
const cur = await fs5.readFile(gf.file, "utf8").catch(() => null);
|
|
2864
|
+
if (cur === gf.contents) continue;
|
|
2865
|
+
}
|
|
2866
|
+
await writeFileAtomic(gf.file, gf.contents);
|
|
2867
|
+
writtenFiles.push(gf.file);
|
|
2868
|
+
if (!already) createdFiles.push(gf.file);
|
|
2869
|
+
}
|
|
2727
2870
|
for (const file of touched) {
|
|
2728
2871
|
const raw = originals.get(file);
|
|
2729
2872
|
if (raw === void 0) {
|
|
@@ -2742,15 +2885,22 @@ async function apply2(installPlan) {
|
|
|
2742
2885
|
await applyProcfile(file, edits, raw);
|
|
2743
2886
|
writtenFiles.push(file);
|
|
2744
2887
|
}
|
|
2888
|
+
} else if (file === entryFile) {
|
|
2889
|
+
const injected = injectNeatImport(raw);
|
|
2890
|
+
if (injected !== null) {
|
|
2891
|
+
await writeFileAtomic(file, injected);
|
|
2892
|
+
writtenFiles.push(file);
|
|
2893
|
+
}
|
|
2745
2894
|
}
|
|
2746
2895
|
}
|
|
2747
2896
|
} catch (err) {
|
|
2748
|
-
await rollback2(installPlan, originals);
|
|
2897
|
+
await rollback2(installPlan, originals, createdFiles);
|
|
2749
2898
|
throw err;
|
|
2750
2899
|
}
|
|
2751
|
-
|
|
2900
|
+
const outcome = writtenFiles.length > 0 ? "instrumented" : "already-instrumented";
|
|
2901
|
+
return { serviceDir, outcome, writtenFiles };
|
|
2752
2902
|
}
|
|
2753
|
-
async function rollback2(installPlan, originals) {
|
|
2903
|
+
async function rollback2(installPlan, originals, createdFiles = []) {
|
|
2754
2904
|
const restored = [];
|
|
2755
2905
|
for (const [file, raw] of originals.entries()) {
|
|
2756
2906
|
try {
|
|
@@ -2759,6 +2909,14 @@ async function rollback2(installPlan, originals) {
|
|
|
2759
2909
|
} catch {
|
|
2760
2910
|
}
|
|
2761
2911
|
}
|
|
2912
|
+
const removed = [];
|
|
2913
|
+
for (const file of createdFiles) {
|
|
2914
|
+
try {
|
|
2915
|
+
await fs5.rm(file, { force: true });
|
|
2916
|
+
removed.push(file);
|
|
2917
|
+
} catch {
|
|
2918
|
+
}
|
|
2919
|
+
}
|
|
2762
2920
|
const lines = [
|
|
2763
2921
|
"# neat-rollback.patch",
|
|
2764
2922
|
"",
|
|
@@ -2766,6 +2924,7 @@ async function rollback2(installPlan, originals) {
|
|
|
2766
2924
|
"# Files listed below were restored to their pre-apply contents.",
|
|
2767
2925
|
"",
|
|
2768
2926
|
...restored.map((f) => `restored: ${f}`),
|
|
2927
|
+
...removed.map((f) => `removed: ${f}`),
|
|
2769
2928
|
""
|
|
2770
2929
|
];
|
|
2771
2930
|
const rollbackPath = path6.join(installPlan.serviceDir, "neat-rollback.patch");
|