@neat.is/core 0.5.3 → 0.5.4-dev.20260721
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-I72HTUOG.js → chunk-4RU3AAOI.js} +2 -2
- package/dist/{chunk-CFDPIMRP.js → chunk-I4NZ7PSN.js} +3 -2
- package/dist/chunk-I4NZ7PSN.js.map +1 -0
- package/dist/{chunk-GJHEZC5K.js → chunk-PEFX3DBR.js} +422 -111
- package/dist/chunk-PEFX3DBR.js.map +1 -0
- package/dist/{chunk-X2AMX3QZ.js → chunk-VR73QNJD.js} +263 -10
- package/dist/chunk-VR73QNJD.js.map +1 -0
- package/dist/cli.cjs +1094 -448
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +95 -19
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +798 -282
- 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 +807 -291
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-IDMIH6ZY.js → otel-grpc-TEGOXE6T.js} +3 -3
- package/dist/server.cjs +498 -172
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-CFDPIMRP.js.map +0 -1
- package/dist/chunk-GJHEZC5K.js.map +0 -1
- package/dist/chunk-X2AMX3QZ.js.map +0 -1
- /package/dist/{chunk-I72HTUOG.js.map → chunk-4RU3AAOI.js.map} +0 -0
- /package/dist/{otel-grpc-IDMIH6ZY.js.map → otel-grpc-TEGOXE6T.js.map} +0 -0
|
@@ -31,13 +31,13 @@ import {
|
|
|
31
31
|
touchLastSeen,
|
|
32
32
|
upsertObservedEdge,
|
|
33
33
|
writeAtomically
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-PEFX3DBR.js";
|
|
35
35
|
import {
|
|
36
36
|
assertBindAuthority,
|
|
37
37
|
buildOtelReceiver,
|
|
38
38
|
listenSteppingOtlp,
|
|
39
39
|
readAuthEnv
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-I4NZ7PSN.js";
|
|
41
41
|
|
|
42
42
|
// src/daemon.ts
|
|
43
43
|
import {
|
|
@@ -240,7 +240,12 @@ var JUNCTION_DEFAULT_RATE_LIMITS = {
|
|
|
240
240
|
supabase: { capacity: 30, refillMs: 1e4 },
|
|
241
241
|
// Not a documented API limit at all — a self-imposed ceiling on the raw
|
|
242
242
|
// pg_stat_statements connection (see module header above).
|
|
243
|
-
"supabase-postgres": { capacity: 20, refillMs: 3e3 }
|
|
243
|
+
"supabase-postgres": { capacity: 20, refillMs: 3e3 },
|
|
244
|
+
// Push provider (ADR-146): the Drains REST API is touched only by `neat
|
|
245
|
+
// connector add/remove/test` (provision/deprovision/validate), never a poll
|
|
246
|
+
// loop, so this bucket is exercised a handful of times per command. Kept
|
|
247
|
+
// conservative pending a documented Drains-API rate limit.
|
|
248
|
+
vercel: { capacity: 20, refillMs: 5e3 }
|
|
244
249
|
};
|
|
245
250
|
var JUNCTION_GENERIC_RATE_LIMIT = { capacity: 20, refillMs: 5e3 };
|
|
246
251
|
function defaultRateLimitFor(provider) {
|
|
@@ -1443,6 +1448,108 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
1443
1448
|
};
|
|
1444
1449
|
}
|
|
1445
1450
|
|
|
1451
|
+
// src/connectors/vercel/client.ts
|
|
1452
|
+
var DEFAULT_API_BASE_URL = "https://api.vercel.com";
|
|
1453
|
+
var DEFAULT_DRAIN_NAME = "neat-otlp";
|
|
1454
|
+
var TRACE_SCHEMAS = { trace: { version: "v1" } };
|
|
1455
|
+
function apiBase(config) {
|
|
1456
|
+
return config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
|
|
1457
|
+
}
|
|
1458
|
+
function teamQuery(config) {
|
|
1459
|
+
return `?teamId=${encodeURIComponent(config.teamId)}`;
|
|
1460
|
+
}
|
|
1461
|
+
function drainDelivery(config, otelToken) {
|
|
1462
|
+
return {
|
|
1463
|
+
type: "http",
|
|
1464
|
+
endpoint: config.endpoint,
|
|
1465
|
+
encoding: "json",
|
|
1466
|
+
headers: bearerAuthHeader(otelToken),
|
|
1467
|
+
...config.secret ? { secret: config.secret } : {}
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
async function describeError(res) {
|
|
1471
|
+
try {
|
|
1472
|
+
const data = await res.json();
|
|
1473
|
+
const message = data?.error?.message;
|
|
1474
|
+
return typeof message === "string" && message.length > 0 ? ` \u2014 ${message}` : "";
|
|
1475
|
+
} catch {
|
|
1476
|
+
return "";
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
async function createVercelDrain(config, credentials, fetchImpl = fetch) {
|
|
1480
|
+
const projectIds = config.projectIds ?? [];
|
|
1481
|
+
const body = {
|
|
1482
|
+
name: config.drainName ?? DEFAULT_DRAIN_NAME,
|
|
1483
|
+
projects: projectIds.length > 0 ? "some" : "all",
|
|
1484
|
+
...projectIds.length > 0 ? { projectIds } : {},
|
|
1485
|
+
schemas: TRACE_SCHEMAS,
|
|
1486
|
+
delivery: drainDelivery(config, credentials.otelToken),
|
|
1487
|
+
source: { kind: "self-served" }
|
|
1488
|
+
};
|
|
1489
|
+
const res = await junctionFetch(
|
|
1490
|
+
`${apiBase(config)}/v1/drains${teamQuery(config)}`,
|
|
1491
|
+
{
|
|
1492
|
+
method: "POST",
|
|
1493
|
+
headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
|
|
1494
|
+
body: JSON.stringify(body)
|
|
1495
|
+
},
|
|
1496
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
1497
|
+
);
|
|
1498
|
+
if (!res.ok) {
|
|
1499
|
+
throw new Error(
|
|
1500
|
+
`vercel connector: create drain failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
const payload = await res.json().catch(() => null);
|
|
1504
|
+
if (!payload || typeof payload.id !== "string" || payload.id.length === 0) {
|
|
1505
|
+
throw new Error(
|
|
1506
|
+
"vercel connector: create drain returned no drain id \u2014 the Drains API response shape may have changed"
|
|
1507
|
+
);
|
|
1508
|
+
}
|
|
1509
|
+
return {
|
|
1510
|
+
id: payload.id,
|
|
1511
|
+
...payload.status ? { status: payload.status } : {},
|
|
1512
|
+
...payload.disabledReason ? { disabledReason: payload.disabledReason } : {}
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
async function deleteVercelDrain(config, drainId, credentials, fetchImpl = fetch) {
|
|
1516
|
+
const res = await junctionFetch(
|
|
1517
|
+
`${apiBase(config)}/v1/drains/${encodeURIComponent(drainId)}${teamQuery(config)}`,
|
|
1518
|
+
{ method: "DELETE", headers: { ...bearerAuthHeader(credentials.token) } },
|
|
1519
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
1520
|
+
);
|
|
1521
|
+
if (res.ok || res.status === 404) return;
|
|
1522
|
+
throw new Error(
|
|
1523
|
+
`vercel connector: delete drain failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
1526
|
+
async function testVercelDrainDelivery(config, credentials, fetchImpl = fetch) {
|
|
1527
|
+
const res = await junctionFetch(
|
|
1528
|
+
`${apiBase(config)}/v1/drains/test${teamQuery(config)}`,
|
|
1529
|
+
{
|
|
1530
|
+
method: "POST",
|
|
1531
|
+
headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
|
|
1532
|
+
body: JSON.stringify({ schemas: TRACE_SCHEMAS, delivery: drainDelivery(config, credentials.otelToken) })
|
|
1533
|
+
},
|
|
1534
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
1535
|
+
);
|
|
1536
|
+
if (res.status === 401 || res.status === 403) {
|
|
1537
|
+
return { status: "failure", error: `vercel rejected the API token (HTTP ${res.status})` };
|
|
1538
|
+
}
|
|
1539
|
+
if (!res.ok) {
|
|
1540
|
+
return {
|
|
1541
|
+
status: "failure",
|
|
1542
|
+
error: `vercel drain validation failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
const payload = await res.json().catch(() => null);
|
|
1546
|
+
return {
|
|
1547
|
+
...payload?.status ? { status: payload.status } : {},
|
|
1548
|
+
...payload?.error ? { error: payload.error } : {},
|
|
1549
|
+
...payload?.endpoint ? { endpoint: payload.endpoint } : {}
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1446
1553
|
// src/connectors/registry.ts
|
|
1447
1554
|
var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
|
|
1448
1555
|
async function authProbe(input) {
|
|
@@ -1594,8 +1701,96 @@ var PROVIDER_DISPATCH = {
|
|
|
1594
1701
|
}
|
|
1595
1702
|
}
|
|
1596
1703
|
};
|
|
1597
|
-
function
|
|
1598
|
-
return
|
|
1704
|
+
function vercelCredsFrom(credentials) {
|
|
1705
|
+
return { token: String(credentials.token ?? ""), otelToken: String(credentials.otelToken ?? "") };
|
|
1706
|
+
}
|
|
1707
|
+
function vercelConfigFromOptions(options) {
|
|
1708
|
+
const raw = options.projectIds;
|
|
1709
|
+
let projectIds;
|
|
1710
|
+
if (Array.isArray(raw)) projectIds = raw.filter((p) => typeof p === "string");
|
|
1711
|
+
else if (typeof raw === "string" && raw.trim().length > 0) {
|
|
1712
|
+
projectIds = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
1713
|
+
}
|
|
1714
|
+
return {
|
|
1715
|
+
teamId: String(options.teamId ?? ""),
|
|
1716
|
+
endpoint: String(options.endpoint ?? ""),
|
|
1717
|
+
...projectIds && projectIds.length > 0 ? { projectIds } : {},
|
|
1718
|
+
...typeof options.drainId === "string" ? { drainId: options.drainId } : {},
|
|
1719
|
+
...typeof options.drainName === "string" ? { drainName: options.drainName } : {},
|
|
1720
|
+
...typeof options.apiBaseUrl === "string" ? { apiBaseUrl: options.apiBaseUrl } : {},
|
|
1721
|
+
...typeof options.secret === "string" ? { secret: options.secret } : {}
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
var PUSH_PROVIDER_DISPATCH = {
|
|
1725
|
+
vercel: {
|
|
1726
|
+
provider: "vercel",
|
|
1727
|
+
// The Vercel access token is the "primary" secret a single `--token`
|
|
1728
|
+
// populates; `otelToken` (the daemon's OTLP bearer) is the second field.
|
|
1729
|
+
primaryCredentialKey: "token",
|
|
1730
|
+
requiredCredentialFields: ["token", "otelToken"],
|
|
1731
|
+
// teamId scopes every Drains call; endpoint is where the drain delivers.
|
|
1732
|
+
// projectIds is optional (absent → the drain covers the whole team).
|
|
1733
|
+
requiredOptionFields: ["teamId", "endpoint"],
|
|
1734
|
+
// POST /v1/drains/test — authenticates the token and pings the endpoint
|
|
1735
|
+
// with a sample event, so `success` means the credential is live *and* the
|
|
1736
|
+
// daemon's OTLP endpoint is reachable and accepted the drain's bearer.
|
|
1737
|
+
async validate({ credentials, options, fetchImpl }) {
|
|
1738
|
+
const result = await testVercelDrainDelivery(
|
|
1739
|
+
vercelConfigFromOptions(options),
|
|
1740
|
+
vercelCredsFrom(credentials),
|
|
1741
|
+
fetchImpl
|
|
1742
|
+
);
|
|
1743
|
+
if (result.status === "success") return { ok: true };
|
|
1744
|
+
return {
|
|
1745
|
+
ok: false,
|
|
1746
|
+
reason: result.error ?? `vercel drain delivery test returned "${result.status ?? "no status"}"`
|
|
1747
|
+
};
|
|
1748
|
+
},
|
|
1749
|
+
// POST /v1/drains — creates the trace drain, returns its id to store in
|
|
1750
|
+
// `options.drainId`. A created-but-not-enabled drain is surfaced as a note,
|
|
1751
|
+
// not a failure (the entry still points at a real drain).
|
|
1752
|
+
async provision({ credentials, options, fetchImpl }) {
|
|
1753
|
+
try {
|
|
1754
|
+
const created = await createVercelDrain(
|
|
1755
|
+
vercelConfigFromOptions(options),
|
|
1756
|
+
vercelCredsFrom(credentials),
|
|
1757
|
+
fetchImpl
|
|
1758
|
+
);
|
|
1759
|
+
const note = created.status && created.status !== "enabled" ? `the drain was created but its status is "${created.status}"${created.disabledReason ? ` (${created.disabledReason})` : ""} \u2014 check the Vercel dashboard` : void 0;
|
|
1760
|
+
return { ok: true, options: { drainId: created.id }, ...note ? { note } : {} };
|
|
1761
|
+
} catch (err) {
|
|
1762
|
+
return { ok: false, reason: err.message };
|
|
1763
|
+
}
|
|
1764
|
+
},
|
|
1765
|
+
// DELETE /v1/drains/{id} — idempotent (deleteVercelDrain treats 404 as
|
|
1766
|
+
// success). No recorded drainId → nothing to delete, still a success.
|
|
1767
|
+
async deprovision({ credentials, options, fetchImpl }) {
|
|
1768
|
+
const drainId = typeof options.drainId === "string" ? options.drainId : "";
|
|
1769
|
+
if (!drainId) {
|
|
1770
|
+
return { ok: true, note: "no drain id was recorded \u2014 nothing to delete on the Vercel side" };
|
|
1771
|
+
}
|
|
1772
|
+
try {
|
|
1773
|
+
await deleteVercelDrain(
|
|
1774
|
+
vercelConfigFromOptions(options),
|
|
1775
|
+
drainId,
|
|
1776
|
+
vercelCredsFrom(credentials),
|
|
1777
|
+
fetchImpl
|
|
1778
|
+
);
|
|
1779
|
+
return { ok: true };
|
|
1780
|
+
} catch (err) {
|
|
1781
|
+
return { ok: false, reason: err.message };
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
function isPushProvider(provider) {
|
|
1787
|
+
return provider in PUSH_PROVIDER_DISPATCH;
|
|
1788
|
+
}
|
|
1789
|
+
function getProviderFieldSchema(provider) {
|
|
1790
|
+
return PROVIDER_DISPATCH[provider] ?? PUSH_PROVIDER_DISPATCH[provider];
|
|
1791
|
+
}
|
|
1792
|
+
function knownProviderNames() {
|
|
1793
|
+
return [...Object.keys(PROVIDER_DISPATCH), ...Object.keys(PUSH_PROVIDER_DISPATCH)].sort();
|
|
1599
1794
|
}
|
|
1600
1795
|
function resolveEntryCredentials(dispatch, entry, env) {
|
|
1601
1796
|
let credentials;
|
|
@@ -1619,6 +1814,13 @@ function resolveEntryCredentials(dispatch, entry, env) {
|
|
|
1619
1814
|
function buildRegistration(entry, graph, env = process.env) {
|
|
1620
1815
|
const dispatch = PROVIDER_DISPATCH[entry.provider];
|
|
1621
1816
|
if (!dispatch) {
|
|
1817
|
+
if (isPushProvider(entry.provider)) {
|
|
1818
|
+
return {
|
|
1819
|
+
ok: false,
|
|
1820
|
+
push: true,
|
|
1821
|
+
reason: `push provider "${entry.provider}" ingests via the OTLP receiver \u2014 nothing to poll`
|
|
1822
|
+
};
|
|
1823
|
+
}
|
|
1622
1824
|
return { ok: false, reason: `unknown provider "${entry.provider}"` };
|
|
1623
1825
|
}
|
|
1624
1826
|
const creds = resolveEntryCredentials(dispatch, entry, env);
|
|
@@ -1653,7 +1855,7 @@ function buildRegistration(entry, graph, env = process.env) {
|
|
|
1653
1855
|
};
|
|
1654
1856
|
}
|
|
1655
1857
|
async function validateConnectorEntry(entry, env = process.env, fetchImpl) {
|
|
1656
|
-
const dispatch = PROVIDER_DISPATCH[entry.provider];
|
|
1858
|
+
const dispatch = PROVIDER_DISPATCH[entry.provider] ?? PUSH_PROVIDER_DISPATCH[entry.provider];
|
|
1657
1859
|
if (!dispatch) {
|
|
1658
1860
|
return { status: "unknown-provider", reason: `unknown provider "${entry.provider}"` };
|
|
1659
1861
|
}
|
|
@@ -1694,10 +1896,58 @@ async function loadConnectorRegistrations(input) {
|
|
|
1694
1896
|
if (!connectorMatchesProject(entry, project)) continue;
|
|
1695
1897
|
const result = buildRegistration(entry, graph, env);
|
|
1696
1898
|
if (result.ok) registrations.push(result.registration);
|
|
1697
|
-
else onSkip?.(entry, result.reason);
|
|
1899
|
+
else if (!result.push) onSkip?.(entry, result.reason);
|
|
1698
1900
|
}
|
|
1699
1901
|
return registrations;
|
|
1700
1902
|
}
|
|
1903
|
+
function resolvePushEntry(entry, env) {
|
|
1904
|
+
const dispatch = PUSH_PROVIDER_DISPATCH[entry.provider];
|
|
1905
|
+
if (!dispatch) {
|
|
1906
|
+
return PROVIDER_DISPATCH[entry.provider] ? {
|
|
1907
|
+
ok: false,
|
|
1908
|
+
outcome: {
|
|
1909
|
+
status: "not-push",
|
|
1910
|
+
reason: `provider "${entry.provider}" is polled, not provisioned \u2014 there is no drain to manage`
|
|
1911
|
+
}
|
|
1912
|
+
} : { ok: false, outcome: { status: "unknown-provider", reason: `unknown provider "${entry.provider}"` } };
|
|
1913
|
+
}
|
|
1914
|
+
const creds = resolveEntryCredentials(dispatch, entry, env);
|
|
1915
|
+
if (!creds.ok) {
|
|
1916
|
+
const status = creds.kind === "unset-env" ? "unset-env" : creds.kind === "missing-field" ? "missing-field" : "failed";
|
|
1917
|
+
return { ok: false, outcome: { status, reason: creds.reason } };
|
|
1918
|
+
}
|
|
1919
|
+
const options = entry.options ?? {};
|
|
1920
|
+
const missingOpts = dispatch.requiredOptionFields.filter((k) => !(k in options));
|
|
1921
|
+
if (missingOpts.length > 0) {
|
|
1922
|
+
return {
|
|
1923
|
+
ok: false,
|
|
1924
|
+
outcome: { status: "missing-field", reason: `options missing required field(s): ${missingOpts.join(", ")}` }
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
return { ok: true, dispatch, credentials: creds.credentials, options };
|
|
1928
|
+
}
|
|
1929
|
+
async function provisionConnector(entry, env = process.env, fetchImpl) {
|
|
1930
|
+
const resolved = resolvePushEntry(entry, env);
|
|
1931
|
+
if (!resolved.ok) return resolved.outcome;
|
|
1932
|
+
const result = await resolved.dispatch.provision({
|
|
1933
|
+
credentials: resolved.credentials,
|
|
1934
|
+
options: resolved.options,
|
|
1935
|
+
...fetchImpl ? { fetchImpl } : {}
|
|
1936
|
+
});
|
|
1937
|
+
if (!result.ok) return { status: "failed", reason: result.reason };
|
|
1938
|
+
return { status: "ok", ...result.options ? { options: result.options } : {}, ...result.note ? { note: result.note } : {} };
|
|
1939
|
+
}
|
|
1940
|
+
async function deprovisionConnector(entry, env = process.env, fetchImpl) {
|
|
1941
|
+
const resolved = resolvePushEntry(entry, env);
|
|
1942
|
+
if (!resolved.ok) return resolved.outcome;
|
|
1943
|
+
const result = await resolved.dispatch.deprovision({
|
|
1944
|
+
credentials: resolved.credentials,
|
|
1945
|
+
options: resolved.options,
|
|
1946
|
+
...fetchImpl ? { fetchImpl } : {}
|
|
1947
|
+
});
|
|
1948
|
+
if (!result.ok) return { status: "failed", reason: result.reason };
|
|
1949
|
+
return { status: "ok", ...result.note ? { note: result.note } : {} };
|
|
1950
|
+
}
|
|
1701
1951
|
|
|
1702
1952
|
// src/unrouted.ts
|
|
1703
1953
|
import { promises as fs } from "fs";
|
|
@@ -2493,9 +2743,12 @@ async function startDaemon(opts = {}) {
|
|
|
2493
2743
|
}
|
|
2494
2744
|
|
|
2495
2745
|
export {
|
|
2496
|
-
|
|
2497
|
-
|
|
2746
|
+
isPushProvider,
|
|
2747
|
+
getProviderFieldSchema,
|
|
2748
|
+
knownProviderNames,
|
|
2498
2749
|
validateConnectorEntry,
|
|
2750
|
+
provisionConnector,
|
|
2751
|
+
deprovisionConnector,
|
|
2499
2752
|
readDaemonRecord,
|
|
2500
2753
|
resolveNeatVersion,
|
|
2501
2754
|
writeDaemonRecord,
|
|
@@ -2506,4 +2759,4 @@ export {
|
|
|
2506
2759
|
resolveHost,
|
|
2507
2760
|
startDaemon
|
|
2508
2761
|
};
|
|
2509
|
-
//# sourceMappingURL=chunk-
|
|
2762
|
+
//# sourceMappingURL=chunk-VR73QNJD.js.map
|