@neat.is/core 0.9.20-dev.20260922 → 0.9.21-dev.20260923
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-KIV7OB2K.js → chunk-NFJ4W5T6.js} +168 -7
- package/dist/chunk-NFJ4W5T6.js.map +1 -0
- package/dist/{chunk-F7I3G4KC.js → chunk-SK2FEJKB.js} +39 -2
- package/dist/chunk-SK2FEJKB.js.map +1 -0
- package/dist/cli.cjs +260 -14
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +89 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +200 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +200 -6
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +155 -7
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-F7I3G4KC.js.map +0 -1
- package/dist/chunk-KIV7OB2K.js.map +0 -1
|
@@ -4690,9 +4690,30 @@ function withRecoveredCodeAttrs(attrs, locus) {
|
|
|
4690
4690
|
if (locus.codeLineno !== void 0) attrs[CODE_LINENO_ATTR] = locus.codeLineno;
|
|
4691
4691
|
return attrs;
|
|
4692
4692
|
}
|
|
4693
|
+
var SENSITIVE_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
4694
|
+
"authorization",
|
|
4695
|
+
"proxy-authorization",
|
|
4696
|
+
"cookie",
|
|
4697
|
+
"set-cookie",
|
|
4698
|
+
"x-api-key",
|
|
4699
|
+
"api-key",
|
|
4700
|
+
"x-auth-token",
|
|
4701
|
+
"x-amz-security-token",
|
|
4702
|
+
"x-csrf-token"
|
|
4703
|
+
]);
|
|
4704
|
+
function attrHeaderName(key) {
|
|
4705
|
+
const lower = key.toLowerCase();
|
|
4706
|
+
const marker = ".header.";
|
|
4707
|
+
const at = lower.lastIndexOf(marker);
|
|
4708
|
+
return at >= 0 ? lower.slice(at + marker.length) : lower;
|
|
4709
|
+
}
|
|
4710
|
+
function isSensitiveAttrKey(key) {
|
|
4711
|
+
return SENSITIVE_HEADER_NAMES.has(attrHeaderName(key));
|
|
4712
|
+
}
|
|
4693
4713
|
function sanitizeAttributes(attrs) {
|
|
4694
4714
|
const out = {};
|
|
4695
4715
|
for (const [k, v] of Object.entries(attrs)) {
|
|
4716
|
+
if (isSensitiveAttrKey(k)) continue;
|
|
4696
4717
|
if (typeof v === "bigint") out[k] = v.toString();
|
|
4697
4718
|
else out[k] = v;
|
|
4698
4719
|
}
|
|
@@ -5283,6 +5304,14 @@ async function readErrorFileTail(errorsPath, maxBytes) {
|
|
|
5283
5304
|
await handle.close();
|
|
5284
5305
|
}
|
|
5285
5306
|
}
|
|
5307
|
+
function redactPersistedAttributes(ev) {
|
|
5308
|
+
if (!ev.attributes) return ev;
|
|
5309
|
+
const scrubbed = sanitizeAttributes(ev.attributes);
|
|
5310
|
+
if (Object.keys(scrubbed).length === Object.keys(ev.attributes).length) return ev;
|
|
5311
|
+
if (Object.keys(scrubbed).length > 0) return { ...ev, attributes: scrubbed };
|
|
5312
|
+
const { attributes: _dropped, ...rest } = ev;
|
|
5313
|
+
return rest;
|
|
5314
|
+
}
|
|
5286
5315
|
async function readErrorEvents(errorsPath, opts) {
|
|
5287
5316
|
let raw;
|
|
5288
5317
|
try {
|
|
@@ -5291,7 +5320,7 @@ async function readErrorEvents(errorsPath, opts) {
|
|
|
5291
5320
|
if (err.code === "ENOENT") return [];
|
|
5292
5321
|
throw err;
|
|
5293
5322
|
}
|
|
5294
|
-
const events = raw.split("\n").filter((line) => line.length > 0).map((line) => JSON.parse(line));
|
|
5323
|
+
const events = raw.split("\n").filter((line) => line.length > 0).map((line) => redactPersistedAttributes(JSON.parse(line)));
|
|
5295
5324
|
const deduped = dedupeIncidents(events);
|
|
5296
5325
|
const cap = opts?.limit !== void 0 && opts.limit > 0 ? Math.min(opts.limit, INCIDENT_READ_MAX_EVENTS) : INCIDENT_READ_MAX_EVENTS;
|
|
5297
5326
|
return deduped.length > cap ? deduped.slice(deduped.length - cap) : deduped;
|
|
@@ -18599,6 +18628,9 @@ function startConnectorPollLoop(connector, ctx, graph, resolveTarget, options =
|
|
|
18599
18628
|
};
|
|
18600
18629
|
}
|
|
18601
18630
|
|
|
18631
|
+
// src/connectors/gcp-auth.ts
|
|
18632
|
+
import { createSign } from "crypto";
|
|
18633
|
+
|
|
18602
18634
|
// src/connectors/junction.ts
|
|
18603
18635
|
var buckets = /* @__PURE__ */ new Map();
|
|
18604
18636
|
function bucketMapKey(provider, accountKey) {
|
|
@@ -18859,6 +18891,108 @@ async function dbJunction(run, policy) {
|
|
|
18859
18891
|
}
|
|
18860
18892
|
}
|
|
18861
18893
|
|
|
18894
|
+
// src/connectors/gcp-auth.ts
|
|
18895
|
+
var DEFAULT_TOKEN_URI = "https://oauth2.googleapis.com/token";
|
|
18896
|
+
var JWT_BEARER_GRANT = "urn:ietf:params:oauth:grant-type:jwt-bearer";
|
|
18897
|
+
var ASSERTION_LIFETIME_S = 3600;
|
|
18898
|
+
var DEFAULT_REFRESH_SKEW_MS = 6e4;
|
|
18899
|
+
var GCP_LOGGING_READ_SCOPE = "https://www.googleapis.com/auth/logging.read";
|
|
18900
|
+
function parseServiceAccountKey(json) {
|
|
18901
|
+
let raw;
|
|
18902
|
+
try {
|
|
18903
|
+
raw = JSON.parse(json);
|
|
18904
|
+
} catch {
|
|
18905
|
+
throw new Error("gcp service-account key: not valid JSON");
|
|
18906
|
+
}
|
|
18907
|
+
if (!raw || typeof raw !== "object") {
|
|
18908
|
+
throw new Error("gcp service-account key: expected a JSON object");
|
|
18909
|
+
}
|
|
18910
|
+
const obj = raw;
|
|
18911
|
+
const clientEmail = obj["client_email"];
|
|
18912
|
+
const privateKey = obj["private_key"];
|
|
18913
|
+
const projectId = obj["project_id"];
|
|
18914
|
+
if (typeof clientEmail !== "string" || clientEmail.length === 0) {
|
|
18915
|
+
throw new Error("gcp service-account key: missing client_email");
|
|
18916
|
+
}
|
|
18917
|
+
if (typeof privateKey !== "string" || privateKey.length === 0) {
|
|
18918
|
+
throw new Error("gcp service-account key: missing private_key");
|
|
18919
|
+
}
|
|
18920
|
+
if (typeof projectId !== "string" || projectId.length === 0) {
|
|
18921
|
+
throw new Error("gcp service-account key: missing project_id");
|
|
18922
|
+
}
|
|
18923
|
+
const key = { client_email: clientEmail, private_key: privateKey, project_id: projectId };
|
|
18924
|
+
if (typeof obj["token_uri"] === "string" && obj["token_uri"].length > 0) {
|
|
18925
|
+
key.token_uri = obj["token_uri"];
|
|
18926
|
+
}
|
|
18927
|
+
return key;
|
|
18928
|
+
}
|
|
18929
|
+
function base64url(input) {
|
|
18930
|
+
return Buffer.from(input).toString("base64url");
|
|
18931
|
+
}
|
|
18932
|
+
function buildServiceAccountAssertion(key, scope, tokenUri, nowMs) {
|
|
18933
|
+
const iat = Math.floor(nowMs / 1e3);
|
|
18934
|
+
const header = { alg: "RS256", typ: "JWT" };
|
|
18935
|
+
const claims = {
|
|
18936
|
+
iss: key.client_email,
|
|
18937
|
+
scope,
|
|
18938
|
+
aud: tokenUri,
|
|
18939
|
+
iat,
|
|
18940
|
+
exp: iat + ASSERTION_LIFETIME_S
|
|
18941
|
+
};
|
|
18942
|
+
const signingInput = `${base64url(JSON.stringify(header))}.${base64url(JSON.stringify(claims))}`;
|
|
18943
|
+
const signature = createSign("RSA-SHA256").update(signingInput).end().sign(key.private_key);
|
|
18944
|
+
return `${signingInput}.${base64url(signature)}`;
|
|
18945
|
+
}
|
|
18946
|
+
async function mintGcpAccessToken(key, scope, opts = {}) {
|
|
18947
|
+
const now = opts.now ?? Date.now;
|
|
18948
|
+
const tokenUri = opts.tokenUri ?? key.token_uri ?? DEFAULT_TOKEN_URI;
|
|
18949
|
+
const mintedAtMs = now();
|
|
18950
|
+
const assertion = buildServiceAccountAssertion(key, scope, tokenUri, mintedAtMs);
|
|
18951
|
+
const body = new URLSearchParams({ grant_type: JWT_BEARER_GRANT, assertion }).toString();
|
|
18952
|
+
const res = await junctionFetch(
|
|
18953
|
+
tokenUri,
|
|
18954
|
+
{
|
|
18955
|
+
method: "POST",
|
|
18956
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
18957
|
+
body
|
|
18958
|
+
},
|
|
18959
|
+
// accountKey: the GCP project id — an identifier, safe to log, the same
|
|
18960
|
+
// per-(provider, accountKey) rate-limit bucket key the GCP connectors use.
|
|
18961
|
+
{ provider: "gcp-auth", accountKey: key.project_id, ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {} }
|
|
18962
|
+
);
|
|
18963
|
+
if (!res.ok) {
|
|
18964
|
+
throw new Error(`gcp token mint failed: ${res.status} ${res.statusText}`);
|
|
18965
|
+
}
|
|
18966
|
+
const json = await res.json();
|
|
18967
|
+
const accessToken = json.access_token;
|
|
18968
|
+
if (typeof accessToken !== "string" || accessToken.length === 0) {
|
|
18969
|
+
throw new Error("gcp token mint returned no access_token");
|
|
18970
|
+
}
|
|
18971
|
+
const expiresInS = typeof json.expires_in === "number" && json.expires_in > 0 ? json.expires_in : ASSERTION_LIFETIME_S;
|
|
18972
|
+
return { accessToken, expiresAtMs: mintedAtMs + expiresInS * 1e3 };
|
|
18973
|
+
}
|
|
18974
|
+
function createGcpTokenSource(key, scope, opts = {}) {
|
|
18975
|
+
const now = opts.now ?? Date.now;
|
|
18976
|
+
const skewMs = opts.refreshSkewMs ?? DEFAULT_REFRESH_SKEW_MS;
|
|
18977
|
+
let cached = null;
|
|
18978
|
+
let inFlight = null;
|
|
18979
|
+
const fresh = () => cached !== null && now() < cached.expiresAtMs - skewMs;
|
|
18980
|
+
return async () => {
|
|
18981
|
+
if (!fresh()) {
|
|
18982
|
+
if (!inFlight) {
|
|
18983
|
+
inFlight = mintGcpAccessToken(key, scope, opts).then((minted) => {
|
|
18984
|
+
cached = minted;
|
|
18985
|
+
return minted;
|
|
18986
|
+
}).finally(() => {
|
|
18987
|
+
inFlight = null;
|
|
18988
|
+
});
|
|
18989
|
+
}
|
|
18990
|
+
await inFlight;
|
|
18991
|
+
}
|
|
18992
|
+
return { projectId: key.project_id, accessToken: cached.accessToken };
|
|
18993
|
+
};
|
|
18994
|
+
}
|
|
18995
|
+
|
|
18862
18996
|
// src/connectors/supabase/client.ts
|
|
18863
18997
|
var DEFAULT_SUPABASE_MANAGEMENT_API_URL = "https://api.supabase.com";
|
|
18864
18998
|
var DEFAULT_LOG_LIMIT = 1e3;
|
|
@@ -21665,7 +21799,8 @@ function getProviderFieldSchema(provider) {
|
|
|
21665
21799
|
function knownProviderNames() {
|
|
21666
21800
|
return [...Object.keys(PROVIDER_DISPATCH), ...Object.keys(PUSH_PROVIDER_DISPATCH)].sort();
|
|
21667
21801
|
}
|
|
21668
|
-
|
|
21802
|
+
var GCP_SERVICE_ACCOUNT_PROVIDERS = /* @__PURE__ */ new Set(["cloud-run", "gcp-lb", "firebase"]);
|
|
21803
|
+
function resolveEntryCredentials(dispatch, entry, env, fetchImpl) {
|
|
21669
21804
|
let credentials;
|
|
21670
21805
|
try {
|
|
21671
21806
|
const resolved = resolveCredential(entry.credential, env);
|
|
@@ -21674,6 +21809,15 @@ function resolveEntryCredentials(dispatch, entry, env) {
|
|
|
21674
21809
|
if (err instanceof EnvRefUnsetError) return { ok: false, kind: "unset-env", reason: err.message };
|
|
21675
21810
|
return { ok: false, kind: "error", reason: err.message };
|
|
21676
21811
|
}
|
|
21812
|
+
if (GCP_SERVICE_ACCOUNT_PROVIDERS.has(entry.provider) && typeof credentials.serviceAccountKey === "string" && credentials.serviceAccountKey.length > 0) {
|
|
21813
|
+
try {
|
|
21814
|
+
const key = parseServiceAccountKey(credentials.serviceAccountKey);
|
|
21815
|
+
const source = createGcpTokenSource(key, GCP_LOGGING_READ_SCOPE, fetchImpl ? { fetchImpl } : {});
|
|
21816
|
+
return { ok: true, credentials: { projectId: key.project_id }, refreshCredentials: source };
|
|
21817
|
+
} catch (err) {
|
|
21818
|
+
return { ok: false, kind: "error", reason: err.message };
|
|
21819
|
+
}
|
|
21820
|
+
}
|
|
21677
21821
|
const missingCreds = dispatch.requiredCredentialFields.filter((k) => !credentials[k]);
|
|
21678
21822
|
if (missingCreds.length > 0) {
|
|
21679
21823
|
return {
|
|
@@ -21723,7 +21867,10 @@ function buildRegistration(entry, graph, env = process.env) {
|
|
|
21723
21867
|
connector: built.connector,
|
|
21724
21868
|
credentials,
|
|
21725
21869
|
resolveTarget: built.resolveTarget,
|
|
21726
|
-
...intervalMs !== void 0 ? { intervalMs } : {}
|
|
21870
|
+
...intervalMs !== void 0 ? { intervalMs } : {},
|
|
21871
|
+
// A refreshable credential (ADR-230) resolves to a per-tick token source; carry it so the daemon wires
|
|
21872
|
+
// it to the poll loop's `refreshCredentials`. Absent for a static credential.
|
|
21873
|
+
...creds.refreshCredentials ? { refreshCredentials: creds.refreshCredentials } : {}
|
|
21727
21874
|
}
|
|
21728
21875
|
};
|
|
21729
21876
|
}
|
|
@@ -21732,7 +21879,7 @@ async function validateConnectorEntry(entry, env = process.env, fetchImpl) {
|
|
|
21732
21879
|
if (!dispatch) {
|
|
21733
21880
|
return { status: "unknown-provider", reason: `unknown provider "${entry.provider}"` };
|
|
21734
21881
|
}
|
|
21735
|
-
const creds = resolveEntryCredentials(dispatch, entry, env);
|
|
21882
|
+
const creds = resolveEntryCredentials(dispatch, entry, env, fetchImpl);
|
|
21736
21883
|
if (!creds.ok) {
|
|
21737
21884
|
if (creds.kind === "unset-env") return { status: "unset-env", reason: creds.reason };
|
|
21738
21885
|
return { status: "missing-field", reason: creds.reason };
|
|
@@ -21745,8 +21892,16 @@ async function validateConnectorEntry(entry, env = process.env, fetchImpl) {
|
|
|
21745
21892
|
reason: `options missing required field(s): ${missingOpts.join(", ")}`
|
|
21746
21893
|
};
|
|
21747
21894
|
}
|
|
21895
|
+
let credentials = creds.credentials;
|
|
21896
|
+
if (creds.refreshCredentials) {
|
|
21897
|
+
try {
|
|
21898
|
+
credentials = { ...credentials, ...await creds.refreshCredentials() };
|
|
21899
|
+
} catch (err) {
|
|
21900
|
+
return { status: "rejected", reason: err.message };
|
|
21901
|
+
}
|
|
21902
|
+
}
|
|
21748
21903
|
const result = await dispatch.validate({
|
|
21749
|
-
credentials
|
|
21904
|
+
credentials,
|
|
21750
21905
|
options,
|
|
21751
21906
|
...fetchImpl ? { fetchImpl } : {}
|
|
21752
21907
|
});
|
|
@@ -21792,7 +21947,13 @@ async function startConnectorPolling(input) {
|
|
|
21792
21947
|
},
|
|
21793
21948
|
input.graph,
|
|
21794
21949
|
registration.resolveTarget,
|
|
21795
|
-
{
|
|
21950
|
+
{
|
|
21951
|
+
intervalMs: registration.intervalMs,
|
|
21952
|
+
connectorId: registration.id,
|
|
21953
|
+
// A refreshable credential (ADR-230) mints a fresh token each tick; the static path leaves this unset
|
|
21954
|
+
// and the loop reuses ctx.credentials, exactly as before.
|
|
21955
|
+
...registration.refreshCredentials ? { refreshCredentials: registration.refreshCredentials } : {}
|
|
21956
|
+
}
|
|
21796
21957
|
)
|
|
21797
21958
|
);
|
|
21798
21959
|
return () => {
|
|
@@ -22859,4 +23020,4 @@ export {
|
|
|
22859
23020
|
deprovisionConnector,
|
|
22860
23021
|
buildApi
|
|
22861
23022
|
};
|
|
22862
|
-
//# sourceMappingURL=chunk-
|
|
23023
|
+
//# sourceMappingURL=chunk-NFJ4W5T6.js.map
|