@autter/runtime-node 1.3.4 → 1.5.0

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.
@@ -0,0 +1,202 @@
1
+ import {
2
+ COMPAT_MANIFEST,
3
+ SDK_IDENTITY,
4
+ compatRequest,
5
+ evaluateCompat,
6
+ fetchIngesterCompat,
7
+ versionAtLeast
8
+ } from "./chunk-L7HWKMCZ.js";
9
+
10
+ // src/doctor.ts
11
+ import { existsSync, readFileSync } from "fs";
12
+ import { dirname, join } from "path";
13
+ var SDK_PACKAGES = ["@autter/runtime-node", "@autter/runtime-next", "@autter/runtime-browser"];
14
+ var DEFAULT_ENDPOINT = "https://otlp.autter.dev";
15
+ function resolveEndpoint(flag, env = process.env) {
16
+ return (flag || env.AUTTER_ENDPOINT || env.AUTTER_RUNTIME_ENDPOINT || env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_ENDPOINT).replace(/\/$/, "");
17
+ }
18
+ function installedSdks(cwd) {
19
+ const found = /* @__PURE__ */ new Map();
20
+ for (let dir = cwd; ; dir = dirname(dir)) {
21
+ for (const name of SDK_PACKAGES) {
22
+ if (found.has(name)) continue;
23
+ const file = join(dir, "node_modules", ...name.split("/"), "package.json");
24
+ if (!existsSync(file)) continue;
25
+ try {
26
+ const version = JSON.parse(readFileSync(file, "utf8")).version;
27
+ if (typeof version === "string") found.set(name, version);
28
+ } catch {
29
+ }
30
+ }
31
+ if (dirname(dir) === dir) break;
32
+ }
33
+ if (!found.has(SDK_IDENTITY.name)) found.set(SDK_IDENTITY.name, SDK_IDENTITY.version);
34
+ return [...found].map(([name, version]) => ({ name, version }));
35
+ }
36
+ function featuresInUse(sdks) {
37
+ return COMPAT_MANIFEST.features.filter(
38
+ (feature) => sdks.some((sdk) => {
39
+ const min = feature.sdks[sdk.name];
40
+ return min !== void 0 && versionAtLeast(sdk.version, min) === true;
41
+ })
42
+ ).map((feature) => feature.id);
43
+ }
44
+ async function checkKey(endpoint, key, timeoutMs) {
45
+ const res = await compatRequest("POST", `${endpoint}/v1/traces`, {
46
+ body: JSON.stringify({ resourceSpans: [] }),
47
+ headers: { authorization: `Bearer ${key}` },
48
+ unref: false,
49
+ timeoutMs
50
+ });
51
+ if (!res) return "unknown";
52
+ if (res.status >= 200 && res.status < 300) return "accepted";
53
+ if (res.status === 401) return "rejected";
54
+ if (res.status === 403) return "client_key";
55
+ if (res.status === 503) return "storage_unavailable";
56
+ return "unknown";
57
+ }
58
+ async function runDoctor(options = {}) {
59
+ const endpoint = resolveEndpoint(options.endpoint);
60
+ const timeoutMs = options.timeoutMs ?? 5e3;
61
+ const sdks = installedSdks(options.cwd ?? process.cwd());
62
+ const features = options.features?.length ? options.features : featuresInUse(sdks);
63
+ const routes = [
64
+ ...new Set(
65
+ features.map((id) => COMPAT_MANIFEST.features.find((f) => f.id === id)?.route).filter((route) => Boolean(route))
66
+ )
67
+ ];
68
+ const { info, error } = await fetchIngesterCompat(endpoint, { probeRoutes: routes, unref: false, timeoutMs });
69
+ const issues = [];
70
+ const seen = /* @__PURE__ */ new Set();
71
+ const add = (issue) => {
72
+ const key2 = `${issue.feature}:${issue.kind}:${issue.component}`;
73
+ if (!seen.has(key2)) {
74
+ seen.add(key2);
75
+ issues.push(issue);
76
+ }
77
+ };
78
+ evaluateCompat({ features, ingester: info }).forEach(add);
79
+ for (const sdk of sdks) {
80
+ if (options.features?.length) {
81
+ evaluateCompat({ features: options.features, ingester: null, sdk }).forEach(add);
82
+ }
83
+ }
84
+ const key = options.key ? await checkKey(endpoint, options.key, timeoutMs) : void 0;
85
+ const featureRows = features.flatMap((id) => {
86
+ const feature = COMPAT_MANIFEST.features.find((f) => f.id === id);
87
+ if (!feature) return [];
88
+ const bad = issues.some((issue) => issue.feature === id);
89
+ const known = info?.version != null || feature.route !== void 0 && info?.routes?.[feature.route] !== void 0;
90
+ return [{ id, title: feature.title, status: bad ? "incompatible" : known ? "ok" : "unknown" }];
91
+ });
92
+ const exitCode = !info ? 2 : issues.length || key === "rejected" || key === "client_key" || key === "storage_unavailable" ? 1 : 0;
93
+ return {
94
+ endpoint,
95
+ sdks,
96
+ ingester: info,
97
+ ...error && !info ? { ingesterError: error } : {},
98
+ ...key ? { key } : {},
99
+ features: featureRows,
100
+ issues,
101
+ exitCode
102
+ };
103
+ }
104
+ function formatDoctorReport(report) {
105
+ const lines = [];
106
+ lines.push(`Autter Runtime doctor`);
107
+ lines.push(` endpoint ${report.endpoint}`);
108
+ for (const sdk of report.sdks) lines.push(` sdk ${sdk.name}@${sdk.version}`);
109
+ if (!report.ingester) {
110
+ lines.push(` ingester unreachable or unidentified (${report.ingesterError ?? "unknown error"})`);
111
+ } else if (report.ingester.version) {
112
+ lines.push(` ingester @autter/otlp-ingester@${report.ingester.version}`);
113
+ const schema = report.ingester.schema;
114
+ if (schema) {
115
+ lines.push(
116
+ ` schema ${schema.status}${schema.applied.length ? ` (level ${schema.applied[schema.applied.length - 1]})` : ""}`
117
+ );
118
+ }
119
+ } else {
120
+ lines.push(` ingester 1.4.0 or older (no /v1/compat endpoint; upgrade for a full report)`);
121
+ }
122
+ if (report.key) {
123
+ const text = {
124
+ accepted: "accepted",
125
+ rejected: "REJECTED (401): check AUTTER_RUNTIME_KEY",
126
+ client_key: "is a client/browser key; server SDKs need a server key",
127
+ storage_unavailable: "accepted, but the ingester's ClickHouse is unavailable (503)",
128
+ unknown: "could not be verified"
129
+ }[report.key];
130
+ lines.push(` key ${text}`);
131
+ }
132
+ if (report.features.length) {
133
+ lines.push("", "Features");
134
+ for (const feature of report.features) {
135
+ const mark = feature.status === "ok" ? "ok " : feature.status === "incompatible" ? "FAIL" : "?? ";
136
+ lines.push(` ${mark} ${feature.title} (${feature.id})`);
137
+ }
138
+ }
139
+ if (report.issues.length) {
140
+ lines.push("", "Problems");
141
+ for (const issue of report.issues) lines.push(` - ${issue.message}`);
142
+ }
143
+ lines.push(
144
+ "",
145
+ report.exitCode === 0 ? "Compatible." : report.exitCode === 1 ? "Incompatible: see the problems above." : "Could not check the ingester. Pass --endpoint <url> (or set AUTTER_ENDPOINT)."
146
+ );
147
+ return lines.join("\n");
148
+ }
149
+ var DOCTOR_USAGE = `Usage: npx @autter/runtime-node doctor [options]
150
+
151
+ Checks that your Autter SDKs, ingester and ClickHouse schema versions match.
152
+
153
+ Options:
154
+ --endpoint <url> Ingester base URL (default: $AUTTER_ENDPOINT, $OTEL_EXPORTER_OTLP_ENDPOINT, or ${DEFAULT_ENDPOINT})
155
+ --key <key> Also verify an ingest key (default: $AUTTER_RUNTIME_KEY)
156
+ --features <a,b> Only check these features (${COMPAT_MANIFEST.features.map((f) => f.id).join(", ")})
157
+ --json Machine-readable output
158
+ --timeout <ms> Per-request timeout (default 5000)
159
+
160
+ Exit codes: 0 compatible, 1 mismatch or rejected key, 2 ingester unreachable.`;
161
+ function parseDoctorArgs(argv, env = process.env) {
162
+ const options = {};
163
+ for (let i = 0; i < argv.length; i++) {
164
+ const arg = argv[i];
165
+ const [flag, inline] = arg.includes("=") ? [arg.slice(0, arg.indexOf("=")), arg.slice(arg.indexOf("=") + 1)] : [arg, void 0];
166
+ const value = () => inline ?? argv[++i];
167
+ switch (flag) {
168
+ case "-h":
169
+ case "--help":
170
+ return { help: true };
171
+ case "--endpoint":
172
+ options.endpoint = value();
173
+ break;
174
+ case "--key":
175
+ options.key = value();
176
+ break;
177
+ case "--features":
178
+ options.features = (value() ?? "").split(",").map((f) => f.trim()).filter(Boolean);
179
+ break;
180
+ case "--json":
181
+ options.json = true;
182
+ break;
183
+ case "--timeout": {
184
+ const ms = Number(value());
185
+ if (!Number.isFinite(ms) || ms <= 0) return { error: "--timeout needs a positive number of ms" };
186
+ options.timeoutMs = ms;
187
+ break;
188
+ }
189
+ default:
190
+ return { error: `unknown option ${arg}` };
191
+ }
192
+ }
193
+ if (!options.key && env.AUTTER_RUNTIME_KEY) options.key = env.AUTTER_RUNTIME_KEY;
194
+ return options;
195
+ }
196
+
197
+ export {
198
+ runDoctor,
199
+ formatDoctorReport,
200
+ DOCTOR_USAGE,
201
+ parseDoctorArgs
202
+ };
package/dist/cli.cjs ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+
5
+
6
+
7
+ var _chunkL6SRL65Jcjs = require('./chunk-L6SRL65J.cjs');
8
+ require('./chunk-GI47WGRR.cjs');
9
+
10
+ // src/cli.ts
11
+ async function main(argv) {
12
+ const [command, ...rest] = argv;
13
+ if (command !== "doctor") {
14
+ console.log(_chunkL6SRL65Jcjs.DOCTOR_USAGE);
15
+ return command === void 0 || command === "--help" || command === "-h" ? 0 : 64;
16
+ }
17
+ const parsed = _chunkL6SRL65Jcjs.parseDoctorArgs.call(void 0, rest);
18
+ if ("help" in parsed) {
19
+ console.log(_chunkL6SRL65Jcjs.DOCTOR_USAGE);
20
+ return 0;
21
+ }
22
+ if ("error" in parsed) {
23
+ console.error(`${parsed.error}
24
+
25
+ ${_chunkL6SRL65Jcjs.DOCTOR_USAGE}`);
26
+ return 64;
27
+ }
28
+ const report = await _chunkL6SRL65Jcjs.runDoctor.call(void 0, parsed);
29
+ console.log(parsed.json ? JSON.stringify(report, null, 2) : _chunkL6SRL65Jcjs.formatDoctorReport.call(void 0, report));
30
+ return report.exitCode;
31
+ }
32
+ main(process.argv.slice(2)).then(
33
+ (code) => {
34
+ process.exitCode = code;
35
+ },
36
+ (err) => {
37
+ console.error("autter doctor failed:", err instanceof Error ? err.message : err);
38
+ process.exitCode = 2;
39
+ }
40
+ );
package/dist/cli.d.cts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ DOCTOR_USAGE,
4
+ formatDoctorReport,
5
+ parseDoctorArgs,
6
+ runDoctor
7
+ } from "./chunk-ZS4WQJQR.js";
8
+ import "./chunk-L7HWKMCZ.js";
9
+
10
+ // src/cli.ts
11
+ async function main(argv) {
12
+ const [command, ...rest] = argv;
13
+ if (command !== "doctor") {
14
+ console.log(DOCTOR_USAGE);
15
+ return command === void 0 || command === "--help" || command === "-h" ? 0 : 64;
16
+ }
17
+ const parsed = parseDoctorArgs(rest);
18
+ if ("help" in parsed) {
19
+ console.log(DOCTOR_USAGE);
20
+ return 0;
21
+ }
22
+ if ("error" in parsed) {
23
+ console.error(`${parsed.error}
24
+
25
+ ${DOCTOR_USAGE}`);
26
+ return 64;
27
+ }
28
+ const report = await runDoctor(parsed);
29
+ console.log(parsed.json ? JSON.stringify(report, null, 2) : formatDoctorReport(report));
30
+ return report.exitCode;
31
+ }
32
+ main(process.argv.slice(2)).then(
33
+ (code) => {
34
+ process.exitCode = code;
35
+ },
36
+ (err) => {
37
+ console.error("autter doctor failed:", err instanceof Error ? err.message : err);
38
+ process.exitCode = 2;
39
+ }
40
+ );