@neat.is/core 0.9.20-dev.20260922 → 0.9.20
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
package/dist/index.cjs
CHANGED
|
@@ -6614,9 +6614,30 @@ function withRecoveredCodeAttrs(attrs, locus) {
|
|
|
6614
6614
|
if (locus.codeLineno !== void 0) attrs[CODE_LINENO_ATTR] = locus.codeLineno;
|
|
6615
6615
|
return attrs;
|
|
6616
6616
|
}
|
|
6617
|
+
var SENSITIVE_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
6618
|
+
"authorization",
|
|
6619
|
+
"proxy-authorization",
|
|
6620
|
+
"cookie",
|
|
6621
|
+
"set-cookie",
|
|
6622
|
+
"x-api-key",
|
|
6623
|
+
"api-key",
|
|
6624
|
+
"x-auth-token",
|
|
6625
|
+
"x-amz-security-token",
|
|
6626
|
+
"x-csrf-token"
|
|
6627
|
+
]);
|
|
6628
|
+
function attrHeaderName(key) {
|
|
6629
|
+
const lower = key.toLowerCase();
|
|
6630
|
+
const marker = ".header.";
|
|
6631
|
+
const at = lower.lastIndexOf(marker);
|
|
6632
|
+
return at >= 0 ? lower.slice(at + marker.length) : lower;
|
|
6633
|
+
}
|
|
6634
|
+
function isSensitiveAttrKey(key) {
|
|
6635
|
+
return SENSITIVE_HEADER_NAMES.has(attrHeaderName(key));
|
|
6636
|
+
}
|
|
6617
6637
|
function sanitizeAttributes(attrs) {
|
|
6618
6638
|
const out = {};
|
|
6619
6639
|
for (const [k, v] of Object.entries(attrs)) {
|
|
6640
|
+
if (isSensitiveAttrKey(k)) continue;
|
|
6620
6641
|
if (typeof v === "bigint") out[k] = v.toString();
|
|
6621
6642
|
else out[k] = v;
|
|
6622
6643
|
}
|
|
@@ -7207,6 +7228,14 @@ async function readErrorFileTail(errorsPath, maxBytes) {
|
|
|
7207
7228
|
await handle.close();
|
|
7208
7229
|
}
|
|
7209
7230
|
}
|
|
7231
|
+
function redactPersistedAttributes(ev) {
|
|
7232
|
+
if (!ev.attributes) return ev;
|
|
7233
|
+
const scrubbed = sanitizeAttributes(ev.attributes);
|
|
7234
|
+
if (Object.keys(scrubbed).length === Object.keys(ev.attributes).length) return ev;
|
|
7235
|
+
if (Object.keys(scrubbed).length > 0) return { ...ev, attributes: scrubbed };
|
|
7236
|
+
const { attributes: _dropped, ...rest } = ev;
|
|
7237
|
+
return rest;
|
|
7238
|
+
}
|
|
7210
7239
|
async function readErrorEvents(errorsPath, opts) {
|
|
7211
7240
|
let raw;
|
|
7212
7241
|
try {
|
|
@@ -7215,7 +7244,7 @@ async function readErrorEvents(errorsPath, opts) {
|
|
|
7215
7244
|
if (err.code === "ENOENT") return [];
|
|
7216
7245
|
throw err;
|
|
7217
7246
|
}
|
|
7218
|
-
const events = raw.split("\n").filter((line) => line.length > 0).map((line) => JSON.parse(line));
|
|
7247
|
+
const events = raw.split("\n").filter((line) => line.length > 0).map((line) => redactPersistedAttributes(JSON.parse(line)));
|
|
7219
7248
|
const deduped = dedupeIncidents(events);
|
|
7220
7249
|
const cap = opts?.limit !== void 0 && opts.limit > 0 ? Math.min(opts.limit, INCIDENT_READ_MAX_EVENTS) : INCIDENT_READ_MAX_EVENTS;
|
|
7221
7250
|
return deduped.length > cap ? deduped.slice(deduped.length - cap) : deduped;
|
|
@@ -19195,6 +19224,10 @@ function startConnectorPollLoop(connector, ctx, graph, resolveTarget, options =
|
|
|
19195
19224
|
// src/connectors/registry.ts
|
|
19196
19225
|
init_cjs_shims();
|
|
19197
19226
|
|
|
19227
|
+
// src/connectors/gcp-auth.ts
|
|
19228
|
+
init_cjs_shims();
|
|
19229
|
+
var import_node_crypto3 = require("crypto");
|
|
19230
|
+
|
|
19198
19231
|
// src/connectors/junction.ts
|
|
19199
19232
|
init_cjs_shims();
|
|
19200
19233
|
var buckets = /* @__PURE__ */ new Map();
|
|
@@ -19456,6 +19489,108 @@ async function dbJunction(run, policy) {
|
|
|
19456
19489
|
}
|
|
19457
19490
|
}
|
|
19458
19491
|
|
|
19492
|
+
// src/connectors/gcp-auth.ts
|
|
19493
|
+
var DEFAULT_TOKEN_URI = "https://oauth2.googleapis.com/token";
|
|
19494
|
+
var JWT_BEARER_GRANT = "urn:ietf:params:oauth:grant-type:jwt-bearer";
|
|
19495
|
+
var ASSERTION_LIFETIME_S = 3600;
|
|
19496
|
+
var DEFAULT_REFRESH_SKEW_MS = 6e4;
|
|
19497
|
+
var GCP_LOGGING_READ_SCOPE = "https://www.googleapis.com/auth/logging.read";
|
|
19498
|
+
function parseServiceAccountKey(json) {
|
|
19499
|
+
let raw;
|
|
19500
|
+
try {
|
|
19501
|
+
raw = JSON.parse(json);
|
|
19502
|
+
} catch {
|
|
19503
|
+
throw new Error("gcp service-account key: not valid JSON");
|
|
19504
|
+
}
|
|
19505
|
+
if (!raw || typeof raw !== "object") {
|
|
19506
|
+
throw new Error("gcp service-account key: expected a JSON object");
|
|
19507
|
+
}
|
|
19508
|
+
const obj = raw;
|
|
19509
|
+
const clientEmail = obj["client_email"];
|
|
19510
|
+
const privateKey = obj["private_key"];
|
|
19511
|
+
const projectId = obj["project_id"];
|
|
19512
|
+
if (typeof clientEmail !== "string" || clientEmail.length === 0) {
|
|
19513
|
+
throw new Error("gcp service-account key: missing client_email");
|
|
19514
|
+
}
|
|
19515
|
+
if (typeof privateKey !== "string" || privateKey.length === 0) {
|
|
19516
|
+
throw new Error("gcp service-account key: missing private_key");
|
|
19517
|
+
}
|
|
19518
|
+
if (typeof projectId !== "string" || projectId.length === 0) {
|
|
19519
|
+
throw new Error("gcp service-account key: missing project_id");
|
|
19520
|
+
}
|
|
19521
|
+
const key = { client_email: clientEmail, private_key: privateKey, project_id: projectId };
|
|
19522
|
+
if (typeof obj["token_uri"] === "string" && obj["token_uri"].length > 0) {
|
|
19523
|
+
key.token_uri = obj["token_uri"];
|
|
19524
|
+
}
|
|
19525
|
+
return key;
|
|
19526
|
+
}
|
|
19527
|
+
function base64url(input) {
|
|
19528
|
+
return Buffer.from(input).toString("base64url");
|
|
19529
|
+
}
|
|
19530
|
+
function buildServiceAccountAssertion(key, scope, tokenUri, nowMs) {
|
|
19531
|
+
const iat = Math.floor(nowMs / 1e3);
|
|
19532
|
+
const header = { alg: "RS256", typ: "JWT" };
|
|
19533
|
+
const claims = {
|
|
19534
|
+
iss: key.client_email,
|
|
19535
|
+
scope,
|
|
19536
|
+
aud: tokenUri,
|
|
19537
|
+
iat,
|
|
19538
|
+
exp: iat + ASSERTION_LIFETIME_S
|
|
19539
|
+
};
|
|
19540
|
+
const signingInput = `${base64url(JSON.stringify(header))}.${base64url(JSON.stringify(claims))}`;
|
|
19541
|
+
const signature = (0, import_node_crypto3.createSign)("RSA-SHA256").update(signingInput).end().sign(key.private_key);
|
|
19542
|
+
return `${signingInput}.${base64url(signature)}`;
|
|
19543
|
+
}
|
|
19544
|
+
async function mintGcpAccessToken(key, scope, opts = {}) {
|
|
19545
|
+
const now = opts.now ?? Date.now;
|
|
19546
|
+
const tokenUri = opts.tokenUri ?? key.token_uri ?? DEFAULT_TOKEN_URI;
|
|
19547
|
+
const mintedAtMs = now();
|
|
19548
|
+
const assertion = buildServiceAccountAssertion(key, scope, tokenUri, mintedAtMs);
|
|
19549
|
+
const body = new URLSearchParams({ grant_type: JWT_BEARER_GRANT, assertion }).toString();
|
|
19550
|
+
const res = await junctionFetch(
|
|
19551
|
+
tokenUri,
|
|
19552
|
+
{
|
|
19553
|
+
method: "POST",
|
|
19554
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
19555
|
+
body
|
|
19556
|
+
},
|
|
19557
|
+
// accountKey: the GCP project id — an identifier, safe to log, the same
|
|
19558
|
+
// per-(provider, accountKey) rate-limit bucket key the GCP connectors use.
|
|
19559
|
+
{ provider: "gcp-auth", accountKey: key.project_id, ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {} }
|
|
19560
|
+
);
|
|
19561
|
+
if (!res.ok) {
|
|
19562
|
+
throw new Error(`gcp token mint failed: ${res.status} ${res.statusText}`);
|
|
19563
|
+
}
|
|
19564
|
+
const json = await res.json();
|
|
19565
|
+
const accessToken = json.access_token;
|
|
19566
|
+
if (typeof accessToken !== "string" || accessToken.length === 0) {
|
|
19567
|
+
throw new Error("gcp token mint returned no access_token");
|
|
19568
|
+
}
|
|
19569
|
+
const expiresInS = typeof json.expires_in === "number" && json.expires_in > 0 ? json.expires_in : ASSERTION_LIFETIME_S;
|
|
19570
|
+
return { accessToken, expiresAtMs: mintedAtMs + expiresInS * 1e3 };
|
|
19571
|
+
}
|
|
19572
|
+
function createGcpTokenSource(key, scope, opts = {}) {
|
|
19573
|
+
const now = opts.now ?? Date.now;
|
|
19574
|
+
const skewMs = opts.refreshSkewMs ?? DEFAULT_REFRESH_SKEW_MS;
|
|
19575
|
+
let cached = null;
|
|
19576
|
+
let inFlight = null;
|
|
19577
|
+
const fresh = () => cached !== null && now() < cached.expiresAtMs - skewMs;
|
|
19578
|
+
return async () => {
|
|
19579
|
+
if (!fresh()) {
|
|
19580
|
+
if (!inFlight) {
|
|
19581
|
+
inFlight = mintGcpAccessToken(key, scope, opts).then((minted) => {
|
|
19582
|
+
cached = minted;
|
|
19583
|
+
return minted;
|
|
19584
|
+
}).finally(() => {
|
|
19585
|
+
inFlight = null;
|
|
19586
|
+
});
|
|
19587
|
+
}
|
|
19588
|
+
await inFlight;
|
|
19589
|
+
}
|
|
19590
|
+
return { projectId: key.project_id, accessToken: cached.accessToken };
|
|
19591
|
+
};
|
|
19592
|
+
}
|
|
19593
|
+
|
|
19459
19594
|
// src/connectors/supabase/index.ts
|
|
19460
19595
|
init_cjs_shims();
|
|
19461
19596
|
|
|
@@ -20322,7 +20457,7 @@ var import_types73 = require("@neat.is/types");
|
|
|
20322
20457
|
|
|
20323
20458
|
// src/connectors/cloudflare/client.ts
|
|
20324
20459
|
init_cjs_shims();
|
|
20325
|
-
var
|
|
20460
|
+
var import_node_crypto4 = require("crypto");
|
|
20326
20461
|
var DEFAULT_BASE_URL = "https://api.cloudflare.com/client/v4";
|
|
20327
20462
|
var DEFAULT_EVENT_LIMIT = 1e3;
|
|
20328
20463
|
async function queryWorkerInvocations(ctx, config, window, fetchImpl = fetch) {
|
|
@@ -20335,7 +20470,7 @@ async function queryWorkerInvocations(ctx, config, window, fetchImpl = fetch) {
|
|
|
20335
20470
|
const body = {
|
|
20336
20471
|
// Cloudflare's schema requires an identifier per query even for an
|
|
20337
20472
|
// ad-hoc, unsaved one — a fresh id per tick, never reused.
|
|
20338
|
-
queryId: `neat-connector-${(0,
|
|
20473
|
+
queryId: `neat-connector-${(0, import_node_crypto4.randomUUID)()}`,
|
|
20339
20474
|
timeframe: { from: window.fromMs, to: window.toMs },
|
|
20340
20475
|
view: "events",
|
|
20341
20476
|
limit: config.eventLimit ?? DEFAULT_EVENT_LIMIT,
|
|
@@ -22337,7 +22472,8 @@ var PUSH_PROVIDER_DISPATCH = {
|
|
|
22337
22472
|
function isPushProvider(provider) {
|
|
22338
22473
|
return provider in PUSH_PROVIDER_DISPATCH;
|
|
22339
22474
|
}
|
|
22340
|
-
|
|
22475
|
+
var GCP_SERVICE_ACCOUNT_PROVIDERS = /* @__PURE__ */ new Set(["cloud-run", "gcp-lb", "firebase"]);
|
|
22476
|
+
function resolveEntryCredentials(dispatch, entry, env, fetchImpl) {
|
|
22341
22477
|
let credentials;
|
|
22342
22478
|
try {
|
|
22343
22479
|
const resolved = resolveCredential(entry.credential, env);
|
|
@@ -22346,6 +22482,15 @@ function resolveEntryCredentials(dispatch, entry, env) {
|
|
|
22346
22482
|
if (err instanceof EnvRefUnsetError) return { ok: false, kind: "unset-env", reason: err.message };
|
|
22347
22483
|
return { ok: false, kind: "error", reason: err.message };
|
|
22348
22484
|
}
|
|
22485
|
+
if (GCP_SERVICE_ACCOUNT_PROVIDERS.has(entry.provider) && typeof credentials.serviceAccountKey === "string" && credentials.serviceAccountKey.length > 0) {
|
|
22486
|
+
try {
|
|
22487
|
+
const key = parseServiceAccountKey(credentials.serviceAccountKey);
|
|
22488
|
+
const source = createGcpTokenSource(key, GCP_LOGGING_READ_SCOPE, fetchImpl ? { fetchImpl } : {});
|
|
22489
|
+
return { ok: true, credentials: { projectId: key.project_id }, refreshCredentials: source };
|
|
22490
|
+
} catch (err) {
|
|
22491
|
+
return { ok: false, kind: "error", reason: err.message };
|
|
22492
|
+
}
|
|
22493
|
+
}
|
|
22349
22494
|
const missingCreds = dispatch.requiredCredentialFields.filter((k) => !credentials[k]);
|
|
22350
22495
|
if (missingCreds.length > 0) {
|
|
22351
22496
|
return {
|
|
@@ -22395,7 +22540,10 @@ function buildRegistration(entry, graph, env = process.env) {
|
|
|
22395
22540
|
connector: built.connector,
|
|
22396
22541
|
credentials,
|
|
22397
22542
|
resolveTarget: built.resolveTarget,
|
|
22398
|
-
...intervalMs !== void 0 ? { intervalMs } : {}
|
|
22543
|
+
...intervalMs !== void 0 ? { intervalMs } : {},
|
|
22544
|
+
// A refreshable credential (ADR-230) resolves to a per-tick token source; carry it so the daemon wires
|
|
22545
|
+
// it to the poll loop's `refreshCredentials`. Absent for a static credential.
|
|
22546
|
+
...creds.refreshCredentials ? { refreshCredentials: creds.refreshCredentials } : {}
|
|
22399
22547
|
}
|
|
22400
22548
|
};
|
|
22401
22549
|
}
|
|
@@ -22439,7 +22587,13 @@ async function startConnectorPolling(input) {
|
|
|
22439
22587
|
},
|
|
22440
22588
|
input.graph,
|
|
22441
22589
|
registration.resolveTarget,
|
|
22442
|
-
{
|
|
22590
|
+
{
|
|
22591
|
+
intervalMs: registration.intervalMs,
|
|
22592
|
+
connectorId: registration.id,
|
|
22593
|
+
// A refreshable credential (ADR-230) mints a fresh token each tick; the static path leaves this unset
|
|
22594
|
+
// and the loop reuses ctx.credentials, exactly as before.
|
|
22595
|
+
...registration.refreshCredentials ? { refreshCredentials: registration.refreshCredentials } : {}
|
|
22596
|
+
}
|
|
22443
22597
|
)
|
|
22444
22598
|
);
|
|
22445
22599
|
return () => {
|
|
@@ -23405,6 +23559,37 @@ async function reconcileFrontierSurfaces(graph, errorsPath) {
|
|
|
23405
23559
|
|
|
23406
23560
|
// src/connectors/hosted.ts
|
|
23407
23561
|
init_cjs_shims();
|
|
23562
|
+
|
|
23563
|
+
// src/connectors/railway/target-ref.ts
|
|
23564
|
+
init_cjs_shims();
|
|
23565
|
+
function nonEmptyString(v) {
|
|
23566
|
+
return typeof v === "string" && v.length > 0;
|
|
23567
|
+
}
|
|
23568
|
+
function decodeRailwayTargetRef(ref) {
|
|
23569
|
+
if (typeof ref !== "string" || ref.length === 0) return null;
|
|
23570
|
+
let json;
|
|
23571
|
+
try {
|
|
23572
|
+
json = Buffer.from(ref, "base64url").toString("utf8");
|
|
23573
|
+
} catch {
|
|
23574
|
+
return null;
|
|
23575
|
+
}
|
|
23576
|
+
let parsed;
|
|
23577
|
+
try {
|
|
23578
|
+
parsed = JSON.parse(json);
|
|
23579
|
+
} catch {
|
|
23580
|
+
return null;
|
|
23581
|
+
}
|
|
23582
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
23583
|
+
const obj = parsed;
|
|
23584
|
+
if (!nonEmptyString(obj.environmentId) || !nonEmptyString(obj.serviceId)) return null;
|
|
23585
|
+
return {
|
|
23586
|
+
environmentId: obj.environmentId,
|
|
23587
|
+
serviceId: obj.serviceId,
|
|
23588
|
+
...nonEmptyString(obj.serviceName) ? { serviceName: obj.serviceName } : {}
|
|
23589
|
+
};
|
|
23590
|
+
}
|
|
23591
|
+
|
|
23592
|
+
// src/connectors/hosted.ts
|
|
23408
23593
|
var CREDENTIAL_REFRESH_SKEW_MS = 6e4;
|
|
23409
23594
|
var CP_REQUEST_TIMEOUT_MS = 1e4;
|
|
23410
23595
|
async function cpGet(path79, deps) {
|
|
@@ -23431,6 +23616,15 @@ function hostedOptions(provider, summary, serviceName) {
|
|
|
23431
23616
|
if (!ref) return null;
|
|
23432
23617
|
return { apiProjectRef: ref, nodeRef: `${ref}.supabase.co`, serviceName };
|
|
23433
23618
|
}
|
|
23619
|
+
case "railway": {
|
|
23620
|
+
const target = summary.projectRef ? decodeRailwayTargetRef(summary.projectRef) : null;
|
|
23621
|
+
if (!target) return null;
|
|
23622
|
+
return {
|
|
23623
|
+
environmentId: target.environmentId,
|
|
23624
|
+
serviceId: target.serviceId,
|
|
23625
|
+
serviceNameById: { [target.serviceId]: serviceName }
|
|
23626
|
+
};
|
|
23627
|
+
}
|
|
23434
23628
|
default:
|
|
23435
23629
|
return null;
|
|
23436
23630
|
}
|