@neat.is/core 0.4.28-dev.20260712 → 0.4.28-dev.20260713
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-5T6J3WKC.js → chunk-N7HEYLOF.js} +731 -143
- package/dist/chunk-N7HEYLOF.js.map +1 -0
- package/dist/{chunk-UJ6WBLIE.js → chunk-V2HUBS5J.js} +163 -345
- package/dist/chunk-V2HUBS5J.js.map +1 -0
- package/dist/cli.cjs +1174 -813
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +9 -9
- package/dist/index.cjs +784 -380
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +849 -445
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +507 -91
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-5T6J3WKC.js.map +0 -1
- package/dist/chunk-UJ6WBLIE.js.map +0 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_PROJECT,
|
|
3
|
+
EnvRefUnsetError,
|
|
3
4
|
Projects,
|
|
4
5
|
attachGraphToEventBus,
|
|
5
6
|
buildApi,
|
|
7
|
+
connectorMatchesProject,
|
|
6
8
|
ensureInfraNode,
|
|
7
9
|
ensureObservedFileNode,
|
|
8
10
|
ensureServiceNode,
|
|
@@ -15,9 +17,13 @@ import {
|
|
|
15
17
|
normalizePathTemplate,
|
|
16
18
|
pathsForProject,
|
|
17
19
|
pruneRegistry,
|
|
20
|
+
readConnectorsConfig,
|
|
18
21
|
reconcileObservedRelPath,
|
|
22
|
+
recordConnectorPoll,
|
|
19
23
|
registryPath,
|
|
20
24
|
resetGraph,
|
|
25
|
+
resolveCredential,
|
|
26
|
+
sanitizePollError,
|
|
21
27
|
saveGraphToDisk,
|
|
22
28
|
setStatus,
|
|
23
29
|
startPersistLoop,
|
|
@@ -25,7 +31,7 @@ import {
|
|
|
25
31
|
touchLastSeen,
|
|
26
32
|
upsertObservedEdge,
|
|
27
33
|
writeAtomically
|
|
28
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-N7HEYLOF.js";
|
|
29
35
|
import {
|
|
30
36
|
assertBindAuthority,
|
|
31
37
|
buildOtelReceiver,
|
|
@@ -35,13 +41,13 @@ import {
|
|
|
35
41
|
|
|
36
42
|
// src/daemon.ts
|
|
37
43
|
import {
|
|
38
|
-
promises as
|
|
44
|
+
promises as fs2,
|
|
39
45
|
watch,
|
|
40
46
|
renameSync,
|
|
41
47
|
unlinkSync,
|
|
42
48
|
writeFileSync
|
|
43
49
|
} from "fs";
|
|
44
|
-
import
|
|
50
|
+
import path2 from "path";
|
|
45
51
|
import { createRequire } from "module";
|
|
46
52
|
|
|
47
53
|
// src/connectors/index.ts
|
|
@@ -103,16 +109,31 @@ function startConnectorPollLoop(connector, ctx, graph, resolveTarget, options =
|
|
|
103
109
|
let stopped = false;
|
|
104
110
|
let since = ctx.since;
|
|
105
111
|
const intervalMs = options.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
112
|
+
const connectorId = options.connectorId;
|
|
106
113
|
const onError = options.onError ?? ((err) => console.error(`[neatd] connector poll failed (${connector.provider})`, err));
|
|
107
114
|
const tick = () => {
|
|
108
115
|
if (stopped) return;
|
|
109
116
|
void (async () => {
|
|
110
117
|
const tickStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
111
118
|
try {
|
|
112
|
-
await runConnectorPoll(connector, { ...ctx, since }, graph, resolveTarget);
|
|
119
|
+
const result = await runConnectorPoll(connector, { ...ctx, since }, graph, resolveTarget);
|
|
113
120
|
since = tickStartedAt;
|
|
121
|
+
if (connectorId) {
|
|
122
|
+
recordConnectorPoll(connectorId, {
|
|
123
|
+
outcome: "ok",
|
|
124
|
+
at: tickStartedAt,
|
|
125
|
+
signalsLastPoll: result.signalCount
|
|
126
|
+
});
|
|
127
|
+
}
|
|
114
128
|
} catch (err) {
|
|
115
129
|
onError(err);
|
|
130
|
+
if (connectorId) {
|
|
131
|
+
recordConnectorPoll(connectorId, {
|
|
132
|
+
outcome: "error",
|
|
133
|
+
at: tickStartedAt,
|
|
134
|
+
error: sanitizePollError(err)
|
|
135
|
+
});
|
|
136
|
+
}
|
|
116
137
|
}
|
|
117
138
|
})();
|
|
118
139
|
};
|
|
@@ -403,6 +424,30 @@ function buildEdgeLogsQuery(limit) {
|
|
|
403
424
|
`limit ${safeLimit}`
|
|
404
425
|
].join("\n");
|
|
405
426
|
}
|
|
427
|
+
function logsAllHttpFailureMessage(status) {
|
|
428
|
+
if (status === 401 || status === 403) {
|
|
429
|
+
return `supabase connector: logs.all request rejected (HTTP ${status}). Check the Management API token, its analytics read scope, and --api-project-ref.`;
|
|
430
|
+
}
|
|
431
|
+
if (status === 404) {
|
|
432
|
+
return "supabase connector: logs.all project not found (HTTP 404). Check --api-project-ref.";
|
|
433
|
+
}
|
|
434
|
+
if (status === 429) {
|
|
435
|
+
return "supabase connector: logs.all rate-limited (HTTP 429). The connector will retry on the next poll; reduce poll cadence if this persists.";
|
|
436
|
+
}
|
|
437
|
+
if (status === 400) {
|
|
438
|
+
return "supabase connector: logs.all query rejected (HTTP 400). Provider details redacted; confirm the live log-query dialect before shipping this connector.";
|
|
439
|
+
}
|
|
440
|
+
return `supabase connector: logs.all request failed (HTTP ${status}); provider details redacted.`;
|
|
441
|
+
}
|
|
442
|
+
function providerErrorDetails(error) {
|
|
443
|
+
if (!error || typeof error === "string") return "";
|
|
444
|
+
const parts = [];
|
|
445
|
+
if (typeof error.code === "number") parts.push(`code ${error.code}`);
|
|
446
|
+
if (typeof error.status === "string" && /^[A-Z0-9_.-]+$/.test(error.status)) {
|
|
447
|
+
parts.push(`status ${error.status}`);
|
|
448
|
+
}
|
|
449
|
+
return parts.length > 0 ? ` (${parts.join(", ")})` : "";
|
|
450
|
+
}
|
|
406
451
|
async function fetchSupabaseEdgeLogs(config, token, startIso, endIso, fetchImpl = fetch) {
|
|
407
452
|
const baseUrl = config.managementApiUrl ?? DEFAULT_SUPABASE_MANAGEMENT_API_URL;
|
|
408
453
|
const url = new URL(`${baseUrl}/v1/projects/${config.apiProjectRef}/analytics/endpoints/logs.all`);
|
|
@@ -417,12 +462,18 @@ async function fetchSupabaseEdgeLogs(config, token, startIso, endIso, fetchImpl
|
|
|
417
462
|
{ provider: "supabase", accountKey: config.apiProjectRef, fetchImpl }
|
|
418
463
|
);
|
|
419
464
|
if (!res.ok) {
|
|
420
|
-
throw new Error(
|
|
465
|
+
throw new Error(logsAllHttpFailureMessage(res.status));
|
|
466
|
+
}
|
|
467
|
+
let body;
|
|
468
|
+
try {
|
|
469
|
+
body = await res.json();
|
|
470
|
+
} catch {
|
|
471
|
+
throw new Error("supabase connector: logs.all returned invalid JSON; provider details redacted.");
|
|
421
472
|
}
|
|
422
|
-
const body = await res.json();
|
|
423
473
|
if (body.error) {
|
|
424
|
-
|
|
425
|
-
|
|
474
|
+
throw new Error(
|
|
475
|
+
`supabase connector: logs.all returned a provider error${providerErrorDetails(body.error)}; provider message redacted.`
|
|
476
|
+
);
|
|
426
477
|
}
|
|
427
478
|
return body.result ?? [];
|
|
428
479
|
}
|
|
@@ -450,10 +501,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
450
501
|
// src/connectors/supabase/map.ts
|
|
451
502
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
452
503
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
453
|
-
function targetFromRestPath(
|
|
454
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
504
|
+
function targetFromRestPath(path3) {
|
|
505
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path3);
|
|
455
506
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
456
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
507
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path3);
|
|
457
508
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
458
509
|
return null;
|
|
459
510
|
}
|
|
@@ -574,6 +625,35 @@ function createSupabaseResolveTarget(graph, config) {
|
|
|
574
625
|
|
|
575
626
|
// src/connectors/supabase/index.ts
|
|
576
627
|
var DEFAULT_MAX_LOOKBACK_MS = 24 * 60 * 60 * 1e3;
|
|
628
|
+
function errorCode(err) {
|
|
629
|
+
const code = err?.code;
|
|
630
|
+
return typeof code === "string" && code.length > 0 ? code : void 0;
|
|
631
|
+
}
|
|
632
|
+
function describeSupabasePostgresSurfaceFailure(projectRef, err) {
|
|
633
|
+
const code = errorCode(err);
|
|
634
|
+
let reason;
|
|
635
|
+
switch (code) {
|
|
636
|
+
case "42501":
|
|
637
|
+
reason = "permission denied; grant pg_read_all_stats to the configured Postgres role";
|
|
638
|
+
break;
|
|
639
|
+
case "42P01":
|
|
640
|
+
case "42704":
|
|
641
|
+
reason = "pg_stat_statements is not enabled or visible to the configured Postgres role";
|
|
642
|
+
break;
|
|
643
|
+
case "28P01":
|
|
644
|
+
case "28000":
|
|
645
|
+
reason = "Postgres credential rejected";
|
|
646
|
+
break;
|
|
647
|
+
case "3D000":
|
|
648
|
+
reason = "database not found";
|
|
649
|
+
break;
|
|
650
|
+
default: {
|
|
651
|
+
const name = err instanceof Error && err.name ? err.name : "Error";
|
|
652
|
+
reason = code ? `${name} ${code}` : name;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return `supabase connector: pg_stat_statements surface unavailable for project ${projectRef} (${reason}); continuing with Management API log surface.`;
|
|
656
|
+
}
|
|
577
657
|
var SupabaseConnector = class {
|
|
578
658
|
// `deps.fetchPgStatStatements` defaults to the real Postgres-backed
|
|
579
659
|
// implementation; tests override it to exercise the "both surfaces
|
|
@@ -605,12 +685,18 @@ var SupabaseConnector = class {
|
|
|
605
685
|
const signals = mapEdgeLogRowsToSignals(logRows);
|
|
606
686
|
if (creds.postgresConnectionString) {
|
|
607
687
|
const fetchStatements = this.deps.fetchPgStatStatements ?? fetchPgStatStatements;
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
688
|
+
try {
|
|
689
|
+
const statementRows = await fetchStatements(
|
|
690
|
+
creds.postgresConnectionString,
|
|
691
|
+
this.config.statementLimit ?? DEFAULT_STATEMENT_LIMIT,
|
|
692
|
+
this.config.apiProjectRef
|
|
693
|
+
);
|
|
694
|
+
signals.push(...diffPgStatStatementsToSignals(statementRows, this.statementBaselines, now.toISOString()));
|
|
695
|
+
} catch (err) {
|
|
696
|
+
const summary = describeSupabasePostgresSurfaceFailure(this.config.apiProjectRef, err);
|
|
697
|
+
if (this.deps.onPostgresSurfaceError) this.deps.onPostgresSurfaceError(err, summary);
|
|
698
|
+
else console.warn(summary);
|
|
699
|
+
}
|
|
614
700
|
}
|
|
615
701
|
return signals;
|
|
616
702
|
}
|
|
@@ -978,9 +1064,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
978
1064
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
979
1065
|
if (secondSep === -1) return null;
|
|
980
1066
|
const method = rest.slice(0, secondSep);
|
|
981
|
-
const
|
|
982
|
-
if (!resourceName || !method || !
|
|
983
|
-
return { resourceName, method, path:
|
|
1067
|
+
const path3 = rest.slice(secondSep + 1);
|
|
1068
|
+
if (!resourceName || !method || !path3) return null;
|
|
1069
|
+
return { resourceName, method, path: path3 };
|
|
984
1070
|
}
|
|
985
1071
|
function resourceNameFor(type, labels) {
|
|
986
1072
|
if (!labels) return null;
|
|
@@ -1017,14 +1103,14 @@ function mapLogEntryToSignal(entry) {
|
|
|
1017
1103
|
if (!req) return null;
|
|
1018
1104
|
if (!req.requestMethod) return null;
|
|
1019
1105
|
const method = req.requestMethod.toUpperCase();
|
|
1020
|
-
const
|
|
1021
|
-
if (
|
|
1106
|
+
const path3 = pathFromRequestUrl(req.requestUrl);
|
|
1107
|
+
if (path3 === null) return null;
|
|
1022
1108
|
const timestamp = entry.timestamp;
|
|
1023
1109
|
if (!timestamp) return null;
|
|
1024
1110
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
1025
1111
|
return {
|
|
1026
1112
|
targetKind: resourceType,
|
|
1027
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
1113
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path3 }),
|
|
1028
1114
|
callCount: 1,
|
|
1029
1115
|
errorCount: isError ? 1 : 0,
|
|
1030
1116
|
lastObservedIso: timestamp
|
|
@@ -1212,7 +1298,7 @@ function mapEventToSignal(event) {
|
|
|
1212
1298
|
if (typeof timestampMs !== "number" || !Number.isFinite(timestampMs)) return null;
|
|
1213
1299
|
const statusCode = metadata?.statusCode;
|
|
1214
1300
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
1215
|
-
const
|
|
1301
|
+
const path3 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
1216
1302
|
return {
|
|
1217
1303
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
1218
1304
|
targetName: scriptName,
|
|
@@ -1220,7 +1306,7 @@ function mapEventToSignal(event) {
|
|
|
1220
1306
|
errorCount: isError ? 1 : 0,
|
|
1221
1307
|
lastObservedIso: new Date(timestampMs).toISOString(),
|
|
1222
1308
|
method,
|
|
1223
|
-
...
|
|
1309
|
+
...path3 ? { path: path3 } : {},
|
|
1224
1310
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
1225
1311
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
1226
1312
|
};
|
|
@@ -1266,8 +1352,8 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
1266
1352
|
});
|
|
1267
1353
|
return found;
|
|
1268
1354
|
}
|
|
1269
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
1270
|
-
const normalizedPath = normalizePathTemplate(
|
|
1355
|
+
function findMatchingRouteNode(graph, serviceName, method, path3) {
|
|
1356
|
+
const normalizedPath = normalizePathTemplate(path3);
|
|
1271
1357
|
let found = null;
|
|
1272
1358
|
graph.forEachNode((id, attrs) => {
|
|
1273
1359
|
if (found) return;
|
|
@@ -1284,10 +1370,10 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
1284
1370
|
return (signal) => {
|
|
1285
1371
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
1286
1372
|
const scriptName = signal.targetName;
|
|
1287
|
-
const { method, path:
|
|
1373
|
+
const { method, path: path3 } = signal;
|
|
1288
1374
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
1289
|
-
if (!method || !
|
|
1290
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
1375
|
+
if (!method || !path3) return wholeFileId;
|
|
1376
|
+
return findMatchingRouteNode(graph, serviceName, method, path3) ?? wholeFileId;
|
|
1291
1377
|
};
|
|
1292
1378
|
const mapping = config.workers?.[scriptName];
|
|
1293
1379
|
if (mapping) {
|
|
@@ -1316,280 +1402,6 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
1316
1402
|
};
|
|
1317
1403
|
}
|
|
1318
1404
|
|
|
1319
|
-
// src/connectors-config.ts
|
|
1320
|
-
import os from "os";
|
|
1321
|
-
import path from "path";
|
|
1322
|
-
import { promises as fs } from "fs";
|
|
1323
|
-
var CONNECTORS_CONFIG_VERSION = 1;
|
|
1324
|
-
var EnvRefUnsetError = class extends Error {
|
|
1325
|
-
ref;
|
|
1326
|
-
varName;
|
|
1327
|
-
constructor(ref, varName) {
|
|
1328
|
-
super(`${ref} is unset`);
|
|
1329
|
-
this.name = "EnvRefUnsetError";
|
|
1330
|
-
this.ref = ref;
|
|
1331
|
-
this.varName = varName;
|
|
1332
|
-
}
|
|
1333
|
-
};
|
|
1334
|
-
function neatHome() {
|
|
1335
|
-
const override = process.env.NEAT_HOME;
|
|
1336
|
-
if (override && override.length > 0) return path.resolve(override);
|
|
1337
|
-
return path.join(os.homedir(), ".neat");
|
|
1338
|
-
}
|
|
1339
|
-
function connectorsConfigPath(home = neatHome()) {
|
|
1340
|
-
return path.join(home, "connectors.json");
|
|
1341
|
-
}
|
|
1342
|
-
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
1343
|
-
async function warnIfModeLooserThan0600(file) {
|
|
1344
|
-
if (process.platform === "win32") return;
|
|
1345
|
-
try {
|
|
1346
|
-
const stat = await fs.stat(file);
|
|
1347
|
-
if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
|
|
1348
|
-
const mode = (stat.mode & 511).toString(8).padStart(3, "0");
|
|
1349
|
-
console.warn(
|
|
1350
|
-
`[neat] ${file} is mode 0${mode}, looser than the 0600 this file's secrets call for \u2014 run \`chmod 600 ${file}\``
|
|
1351
|
-
);
|
|
1352
|
-
}
|
|
1353
|
-
} catch {
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
async function readConnectorsConfig(home = neatHome()) {
|
|
1357
|
-
const file = connectorsConfigPath(home);
|
|
1358
|
-
let raw;
|
|
1359
|
-
try {
|
|
1360
|
-
raw = await fs.readFile(file, "utf8");
|
|
1361
|
-
} catch (err) {
|
|
1362
|
-
if (err.code === "ENOENT") {
|
|
1363
|
-
return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
|
|
1364
|
-
}
|
|
1365
|
-
throw err;
|
|
1366
|
-
}
|
|
1367
|
-
await warnIfModeLooserThan0600(file);
|
|
1368
|
-
let parsed;
|
|
1369
|
-
try {
|
|
1370
|
-
parsed = JSON.parse(raw);
|
|
1371
|
-
} catch (err) {
|
|
1372
|
-
throw new Error(`${file} is not valid JSON: ${err.message}`);
|
|
1373
|
-
}
|
|
1374
|
-
return validateConfig(parsed, file);
|
|
1375
|
-
}
|
|
1376
|
-
function validateConfig(parsed, file) {
|
|
1377
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1378
|
-
throw new Error(`${file} must be a JSON object with a "connectors" array`);
|
|
1379
|
-
}
|
|
1380
|
-
const obj = parsed;
|
|
1381
|
-
const version = obj.version === void 0 ? CONNECTORS_CONFIG_VERSION : obj.version;
|
|
1382
|
-
if (typeof version !== "number" || !Number.isInteger(version)) {
|
|
1383
|
-
throw new Error(`${file}: "version" must be an integer`);
|
|
1384
|
-
}
|
|
1385
|
-
const rawConnectors = obj.connectors;
|
|
1386
|
-
if (!Array.isArray(rawConnectors)) {
|
|
1387
|
-
throw new Error(`${file}: "connectors" must be an array`);
|
|
1388
|
-
}
|
|
1389
|
-
const connectors = rawConnectors.map((entry, i) => validateEntry(entry, i, file));
|
|
1390
|
-
return { version, connectors };
|
|
1391
|
-
}
|
|
1392
|
-
function validateEntry(entry, index, file) {
|
|
1393
|
-
const where = `${file}: connectors[${index}]`;
|
|
1394
|
-
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
1395
|
-
throw new Error(`${where} must be an object`);
|
|
1396
|
-
}
|
|
1397
|
-
const e = entry;
|
|
1398
|
-
const id = e.id;
|
|
1399
|
-
if (typeof id !== "string" || id.length === 0) {
|
|
1400
|
-
throw new Error(`${where}.id must be a non-empty string`);
|
|
1401
|
-
}
|
|
1402
|
-
const provider = e.provider;
|
|
1403
|
-
if (typeof provider !== "string" || provider.length === 0) {
|
|
1404
|
-
throw new Error(`${where}.provider must be a non-empty string`);
|
|
1405
|
-
}
|
|
1406
|
-
if (e.project !== void 0 && typeof e.project !== "string") {
|
|
1407
|
-
throw new Error(`${where}.project must be a string when present`);
|
|
1408
|
-
}
|
|
1409
|
-
const credential = validateCredentialRef(e.credential, `${where}.credential`);
|
|
1410
|
-
let options;
|
|
1411
|
-
if (e.options !== void 0) {
|
|
1412
|
-
if (typeof e.options !== "object" || e.options === null || Array.isArray(e.options)) {
|
|
1413
|
-
throw new Error(`${where}.options must be an object when present`);
|
|
1414
|
-
}
|
|
1415
|
-
options = e.options;
|
|
1416
|
-
}
|
|
1417
|
-
return {
|
|
1418
|
-
id,
|
|
1419
|
-
provider,
|
|
1420
|
-
...typeof e.project === "string" ? { project: e.project } : {},
|
|
1421
|
-
credential,
|
|
1422
|
-
...options ? { options } : {}
|
|
1423
|
-
};
|
|
1424
|
-
}
|
|
1425
|
-
function validateCredentialRef(raw, where) {
|
|
1426
|
-
if (typeof raw === "string") {
|
|
1427
|
-
if (raw.length === 0) throw new Error(`${where} must be a non-empty string`);
|
|
1428
|
-
return raw;
|
|
1429
|
-
}
|
|
1430
|
-
if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
|
|
1431
|
-
const fields = raw;
|
|
1432
|
-
const keys = Object.keys(fields);
|
|
1433
|
-
if (keys.length === 0) throw new Error(`${where} object must have at least one field`);
|
|
1434
|
-
for (const key of keys) {
|
|
1435
|
-
const v = fields[key];
|
|
1436
|
-
if (typeof v !== "string" || v.length === 0) {
|
|
1437
|
-
throw new Error(`${where}.${key} must be a non-empty string`);
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
return fields;
|
|
1441
|
-
}
|
|
1442
|
-
throw new Error(`${where} must be a string or an object of strings`);
|
|
1443
|
-
}
|
|
1444
|
-
function resolveCredential(ref, env = process.env) {
|
|
1445
|
-
if (typeof ref === "string") {
|
|
1446
|
-
return { kind: "single", value: resolveRef(ref, env) };
|
|
1447
|
-
}
|
|
1448
|
-
const fields = {};
|
|
1449
|
-
for (const [key, value] of Object.entries(ref)) {
|
|
1450
|
-
fields[key] = resolveRef(value, env);
|
|
1451
|
-
}
|
|
1452
|
-
return { kind: "fields", fields };
|
|
1453
|
-
}
|
|
1454
|
-
function resolveRef(value, env) {
|
|
1455
|
-
if (value.length > 1 && value.startsWith("$")) {
|
|
1456
|
-
const varName = value.slice(1);
|
|
1457
|
-
const resolved = env[varName];
|
|
1458
|
-
if (resolved === void 0 || resolved.length === 0) {
|
|
1459
|
-
throw new EnvRefUnsetError(value, varName);
|
|
1460
|
-
}
|
|
1461
|
-
return resolved;
|
|
1462
|
-
}
|
|
1463
|
-
return value;
|
|
1464
|
-
}
|
|
1465
|
-
function connectorMatchesProject(entry, project) {
|
|
1466
|
-
return entry.project === void 0 || entry.project === project;
|
|
1467
|
-
}
|
|
1468
|
-
var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
|
|
1469
|
-
var CONNECTORS_LOCK_RETRY_MS = 50;
|
|
1470
|
-
function connectorsConfigLockPath(home = neatHome()) {
|
|
1471
|
-
return path.join(home, "connectors.json.lock");
|
|
1472
|
-
}
|
|
1473
|
-
function isEnvRef(value) {
|
|
1474
|
-
return value.length > 1 && value.startsWith("$");
|
|
1475
|
-
}
|
|
1476
|
-
async function writeConfigAtomically0600(file, contents) {
|
|
1477
|
-
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
1478
|
-
const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
1479
|
-
const fd = await fs.open(tmp, "w", 384);
|
|
1480
|
-
try {
|
|
1481
|
-
await fd.writeFile(contents, "utf8");
|
|
1482
|
-
await fd.chmod(384);
|
|
1483
|
-
await fd.sync();
|
|
1484
|
-
} finally {
|
|
1485
|
-
await fd.close();
|
|
1486
|
-
}
|
|
1487
|
-
await fs.rename(tmp, file);
|
|
1488
|
-
}
|
|
1489
|
-
async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
|
|
1490
|
-
const deadline = Date.now() + timeoutMs;
|
|
1491
|
-
await fs.mkdir(path.dirname(lockPath), { recursive: true });
|
|
1492
|
-
for (; ; ) {
|
|
1493
|
-
try {
|
|
1494
|
-
const fd = await fs.open(lockPath, "wx");
|
|
1495
|
-
try {
|
|
1496
|
-
await fd.writeFile(`${process.pid}
|
|
1497
|
-
`, "utf8");
|
|
1498
|
-
} finally {
|
|
1499
|
-
await fd.close();
|
|
1500
|
-
}
|
|
1501
|
-
return;
|
|
1502
|
-
} catch (err) {
|
|
1503
|
-
if (err.code !== "EEXIST") throw err;
|
|
1504
|
-
if (Date.now() >= deadline) {
|
|
1505
|
-
throw new Error(
|
|
1506
|
-
`neat connector: timed out after ${timeoutMs}ms waiting for ${lockPath}. Another neat process may be writing the connector config; if none is, remove that file by hand.`
|
|
1507
|
-
);
|
|
1508
|
-
}
|
|
1509
|
-
await new Promise((r) => setTimeout(r, CONNECTORS_LOCK_RETRY_MS));
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
async function releaseConnectorsLock(lockPath) {
|
|
1514
|
-
await fs.unlink(lockPath).catch(() => {
|
|
1515
|
-
});
|
|
1516
|
-
}
|
|
1517
|
-
async function withConnectorsLock(home, fn) {
|
|
1518
|
-
const lock = connectorsConfigLockPath(home);
|
|
1519
|
-
await acquireConnectorsLock(lock);
|
|
1520
|
-
try {
|
|
1521
|
-
return await fn();
|
|
1522
|
-
} finally {
|
|
1523
|
-
await releaseConnectorsLock(lock);
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
function serializeConfig(config) {
|
|
1527
|
-
return JSON.stringify(
|
|
1528
|
-
{ version: config.version ?? CONNECTORS_CONFIG_VERSION, connectors: config.connectors },
|
|
1529
|
-
null,
|
|
1530
|
-
2
|
|
1531
|
-
) + "\n";
|
|
1532
|
-
}
|
|
1533
|
-
async function upsertConnectorEntry(entry, home = neatHome()) {
|
|
1534
|
-
const file = connectorsConfigPath(home);
|
|
1535
|
-
return withConnectorsLock(home, async () => {
|
|
1536
|
-
const config = await readConnectorsConfig(home);
|
|
1537
|
-
validateEntry(entry, config.connectors.length, file);
|
|
1538
|
-
const idx = config.connectors.findIndex((c) => c.id === entry.id);
|
|
1539
|
-
const replaced = idx >= 0;
|
|
1540
|
-
if (replaced) config.connectors[idx] = entry;
|
|
1541
|
-
else config.connectors.push(entry);
|
|
1542
|
-
await writeConfigAtomically0600(file, serializeConfig(config));
|
|
1543
|
-
return { replaced };
|
|
1544
|
-
});
|
|
1545
|
-
}
|
|
1546
|
-
async function removeConnectorEntry(id, home = neatHome()) {
|
|
1547
|
-
const file = connectorsConfigPath(home);
|
|
1548
|
-
return withConnectorsLock(home, async () => {
|
|
1549
|
-
const config = await readConnectorsConfig(home);
|
|
1550
|
-
const idx = config.connectors.findIndex((c) => c.id === id);
|
|
1551
|
-
if (idx < 0) return void 0;
|
|
1552
|
-
const [removed] = config.connectors.splice(idx, 1);
|
|
1553
|
-
await writeConfigAtomically0600(file, serializeConfig(config));
|
|
1554
|
-
return removed;
|
|
1555
|
-
});
|
|
1556
|
-
}
|
|
1557
|
-
function slugFragment(value) {
|
|
1558
|
-
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1559
|
-
}
|
|
1560
|
-
function autoSlugConnectorId(provider, project, existingIds) {
|
|
1561
|
-
const base = slugFragment(provider) || "connector";
|
|
1562
|
-
if (!existingIds.has(base)) return base;
|
|
1563
|
-
const projectFrag = project ? slugFragment(project) : "";
|
|
1564
|
-
if (projectFrag) {
|
|
1565
|
-
const withProject = `${base}-${projectFrag}`;
|
|
1566
|
-
if (!existingIds.has(withProject)) return withProject;
|
|
1567
|
-
}
|
|
1568
|
-
const stem = projectFrag ? `${base}-${projectFrag}` : base;
|
|
1569
|
-
for (let n = 2; ; n++) {
|
|
1570
|
-
const candidate = `${stem}-${n}`;
|
|
1571
|
-
if (!existingIds.has(candidate)) return candidate;
|
|
1572
|
-
}
|
|
1573
|
-
}
|
|
1574
|
-
function describeCredential(ref, env = process.env) {
|
|
1575
|
-
const one = (value, field) => {
|
|
1576
|
-
if (isEnvRef(value)) {
|
|
1577
|
-
const varName = value.slice(1);
|
|
1578
|
-
const resolved = env[varName];
|
|
1579
|
-
const set = typeof resolved === "string" && resolved.length > 0;
|
|
1580
|
-
return {
|
|
1581
|
-
...field ? { field } : {},
|
|
1582
|
-
kind: "env-ref",
|
|
1583
|
-
display: value,
|
|
1584
|
-
status: set ? "set" : "unset"
|
|
1585
|
-
};
|
|
1586
|
-
}
|
|
1587
|
-
return { ...field ? { field } : {}, kind: "plaintext", display: "****" };
|
|
1588
|
-
};
|
|
1589
|
-
if (typeof ref === "string") return [one(ref)];
|
|
1590
|
-
return Object.entries(ref).map(([k, v]) => one(v, k));
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
1405
|
// src/connectors/registry.ts
|
|
1594
1406
|
var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
|
|
1595
1407
|
async function authProbe(input) {
|
|
@@ -1778,6 +1590,9 @@ function buildRegistration(entry, graph, env = process.env) {
|
|
|
1778
1590
|
return {
|
|
1779
1591
|
ok: true,
|
|
1780
1592
|
registration: {
|
|
1593
|
+
// Carry the entry id so the daemon can key this connector's poll-status
|
|
1594
|
+
// records to it (ADR-136).
|
|
1595
|
+
id: entry.id,
|
|
1781
1596
|
connector: built.connector,
|
|
1782
1597
|
credentials,
|
|
1783
1598
|
resolveTarget: built.resolveTarget,
|
|
@@ -1833,8 +1648,8 @@ async function loadConnectorRegistrations(input) {
|
|
|
1833
1648
|
}
|
|
1834
1649
|
|
|
1835
1650
|
// src/unrouted.ts
|
|
1836
|
-
import { promises as
|
|
1837
|
-
import
|
|
1651
|
+
import { promises as fs } from "fs";
|
|
1652
|
+
import path from "path";
|
|
1838
1653
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
1839
1654
|
return {
|
|
1840
1655
|
timestamp: now.toISOString(),
|
|
@@ -1843,39 +1658,39 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
1843
1658
|
traceId: traceId ?? null
|
|
1844
1659
|
};
|
|
1845
1660
|
}
|
|
1846
|
-
async function appendUnroutedSpan(
|
|
1847
|
-
const target =
|
|
1848
|
-
await
|
|
1849
|
-
await
|
|
1661
|
+
async function appendUnroutedSpan(neatHome, record) {
|
|
1662
|
+
const target = path.join(neatHome, "errors.ndjson");
|
|
1663
|
+
await fs.mkdir(neatHome, { recursive: true });
|
|
1664
|
+
await fs.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
1850
1665
|
}
|
|
1851
|
-
function unroutedErrorsPath(
|
|
1852
|
-
return
|
|
1666
|
+
function unroutedErrorsPath(neatHome) {
|
|
1667
|
+
return path.join(neatHome, "errors.ndjson");
|
|
1853
1668
|
}
|
|
1854
1669
|
|
|
1855
1670
|
// src/daemon.ts
|
|
1856
1671
|
import { NodeType as NodeType4 } from "@neat.is/types";
|
|
1857
1672
|
function daemonJsonPath(scanPath) {
|
|
1858
|
-
return
|
|
1673
|
+
return path2.join(scanPath, "neat-out", "daemon.json");
|
|
1859
1674
|
}
|
|
1860
1675
|
function daemonsDiscoveryDir(home) {
|
|
1861
1676
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
1862
|
-
return
|
|
1677
|
+
return path2.join(base, "daemons");
|
|
1863
1678
|
}
|
|
1864
1679
|
function daemonDiscoveryPath(project, home) {
|
|
1865
|
-
return
|
|
1680
|
+
return path2.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
1866
1681
|
}
|
|
1867
1682
|
function sanitizeDiscoveryName(project) {
|
|
1868
1683
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
1869
1684
|
}
|
|
1870
1685
|
function neatHomeFromEnv() {
|
|
1871
1686
|
const env = process.env.NEAT_HOME;
|
|
1872
|
-
if (env && env.length > 0) return
|
|
1687
|
+
if (env && env.length > 0) return path2.resolve(env);
|
|
1873
1688
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
1874
|
-
return
|
|
1689
|
+
return path2.join(home, ".neat");
|
|
1875
1690
|
}
|
|
1876
1691
|
async function readDaemonRecord(scanPath) {
|
|
1877
1692
|
try {
|
|
1878
|
-
const raw = await
|
|
1693
|
+
const raw = await fs2.readFile(daemonJsonPath(scanPath), "utf8");
|
|
1879
1694
|
const parsed = JSON.parse(raw);
|
|
1880
1695
|
if (typeof parsed.project === "string" && parsed.ports && typeof parsed.ports.rest === "number" && typeof parsed.ports.otlp === "number" && typeof parsed.ports.web === "number") {
|
|
1881
1696
|
return parsed;
|
|
@@ -1915,7 +1730,7 @@ async function clearDaemonRecord(record, home) {
|
|
|
1915
1730
|
} catch {
|
|
1916
1731
|
}
|
|
1917
1732
|
try {
|
|
1918
|
-
await
|
|
1733
|
+
await fs2.unlink(daemonDiscoveryPath(record.project, home));
|
|
1919
1734
|
} catch {
|
|
1920
1735
|
}
|
|
1921
1736
|
}
|
|
@@ -1952,11 +1767,11 @@ function teardownSlot(slot) {
|
|
|
1952
1767
|
}
|
|
1953
1768
|
}
|
|
1954
1769
|
function neatHomeFor(opts) {
|
|
1955
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
1770
|
+
if (opts.neatHome && opts.neatHome.length > 0) return path2.resolve(opts.neatHome);
|
|
1956
1771
|
const env = process.env.NEAT_HOME;
|
|
1957
|
-
if (env && env.length > 0) return
|
|
1772
|
+
if (env && env.length > 0) return path2.resolve(env);
|
|
1958
1773
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
1959
|
-
return
|
|
1774
|
+
return path2.join(home, ".neat");
|
|
1960
1775
|
}
|
|
1961
1776
|
function routeSpanToProject(serviceName, projects) {
|
|
1962
1777
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -2003,10 +1818,10 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
2003
1818
|
(_id, attrs) => attrs.type === NodeType4.ServiceNode && attrs.name === serviceName
|
|
2004
1819
|
);
|
|
2005
1820
|
}
|
|
2006
|
-
async function bootstrapProject(entry, connectors = [],
|
|
2007
|
-
const paths = pathsForProject(entry.name,
|
|
1821
|
+
async function bootstrapProject(entry, connectors = [], neatHome) {
|
|
1822
|
+
const paths = pathsForProject(entry.name, path2.join(entry.path, "neat-out"));
|
|
2008
1823
|
try {
|
|
2009
|
-
const stat = await
|
|
1824
|
+
const stat = await fs2.stat(entry.path);
|
|
2010
1825
|
if (!stat.isDirectory()) {
|
|
2011
1826
|
throw new Error(`registered path ${entry.path} is not a directory`);
|
|
2012
1827
|
}
|
|
@@ -2044,10 +1859,10 @@ async function bootstrapProject(entry, connectors = [], neatHome2) {
|
|
|
2044
1859
|
staleEventsPath: paths.staleEventsPath,
|
|
2045
1860
|
project: entry.name
|
|
2046
1861
|
});
|
|
2047
|
-
const fileConnectors =
|
|
1862
|
+
const fileConnectors = neatHome ? await loadConnectorRegistrations({
|
|
2048
1863
|
project: entry.name,
|
|
2049
1864
|
graph,
|
|
2050
|
-
home:
|
|
1865
|
+
home: neatHome,
|
|
2051
1866
|
onSkip: (skipped, reason) => console.warn(
|
|
2052
1867
|
`neatd: connector "${skipped.id}" (${skipped.provider}) skipped for project "${entry.name}" \u2014 ${reason}`
|
|
2053
1868
|
)
|
|
@@ -2059,7 +1874,11 @@ async function bootstrapProject(entry, connectors = [], neatHome2) {
|
|
|
2059
1874
|
{ projectDir: entry.path, credentials: registration.credentials },
|
|
2060
1875
|
graph,
|
|
2061
1876
|
registration.resolveTarget,
|
|
2062
|
-
|
|
1877
|
+
// `connectorId` is threaded through so every tick lands in the
|
|
1878
|
+
// in-process status tracker the connector-status endpoint reads
|
|
1879
|
+
// (ADR-136). Undefined for a programmatic registration, which records
|
|
1880
|
+
// nothing.
|
|
1881
|
+
{ intervalMs: registration.intervalMs, connectorId: registration.id }
|
|
2063
1882
|
)
|
|
2064
1883
|
);
|
|
2065
1884
|
const stopConnectors = () => {
|
|
@@ -2131,7 +1950,7 @@ async function startDaemon(opts = {}) {
|
|
|
2131
1950
|
const projectArg = typeof opts.project === "string" && opts.project.length > 0 ? opts.project : process.env.NEAT_PROJECT && process.env.NEAT_PROJECT.length > 0 ? process.env.NEAT_PROJECT : null;
|
|
2132
1951
|
const projectPathArg = opts.projectPath && opts.projectPath.length > 0 ? opts.projectPath : process.env.NEAT_PROJECT_PATH && process.env.NEAT_PROJECT_PATH.length > 0 ? process.env.NEAT_PROJECT_PATH : null;
|
|
2133
1952
|
const singleProject = projectArg;
|
|
2134
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
1953
|
+
const singleProjectPath = singleProject && projectPathArg ? path2.resolve(projectPathArg) : null;
|
|
2135
1954
|
if (singleProject && !singleProjectPath) {
|
|
2136
1955
|
throw new Error(
|
|
2137
1956
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -2139,14 +1958,14 @@ async function startDaemon(opts = {}) {
|
|
|
2139
1958
|
}
|
|
2140
1959
|
if (!singleProject) {
|
|
2141
1960
|
try {
|
|
2142
|
-
await
|
|
1961
|
+
await fs2.access(regPath);
|
|
2143
1962
|
} catch {
|
|
2144
1963
|
throw new Error(
|
|
2145
1964
|
`neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
|
|
2146
1965
|
);
|
|
2147
1966
|
}
|
|
2148
1967
|
}
|
|
2149
|
-
const pidPath =
|
|
1968
|
+
const pidPath = path2.join(home, "neatd.pid");
|
|
2150
1969
|
await writeAtomically(pidPath, `${process.pid}
|
|
2151
1970
|
`);
|
|
2152
1971
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -2328,7 +2147,12 @@ async function startDaemon(opts = {}) {
|
|
|
2328
2147
|
// only this project (the dashboard pins to it), and the daemon-wide
|
|
2329
2148
|
// `/health` carries it at the top level for the spawn-reuse identity
|
|
2330
2149
|
// check. Absent for the legacy multi-project daemon.
|
|
2331
|
-
singleProject: singleProject && singleProjectPath ? { name: singleProject, path: singleProjectPath } : void 0
|
|
2150
|
+
singleProject: singleProject && singleProjectPath ? { name: singleProject, path: singleProjectPath } : void 0,
|
|
2151
|
+
// ADR-136 — the connector-status endpoint reads ~/.neat/connectors.json
|
|
2152
|
+
// through the same resolved home the slot bootstrap read it from, so a
|
|
2153
|
+
// daemon given an explicit NEAT_HOME serves status for the same file it
|
|
2154
|
+
// polls.
|
|
2155
|
+
connectorsHome: home
|
|
2332
2156
|
});
|
|
2333
2157
|
restAddress = await restApp.listen({ port: restPort, host });
|
|
2334
2158
|
console.log(
|
|
@@ -2340,7 +2164,7 @@ async function startDaemon(opts = {}) {
|
|
|
2340
2164
|
}
|
|
2341
2165
|
if (restApp) await restApp.close().catch(() => {
|
|
2342
2166
|
});
|
|
2343
|
-
await
|
|
2167
|
+
await fs2.unlink(pidPath).catch(() => {
|
|
2344
2168
|
});
|
|
2345
2169
|
throw new Error(
|
|
2346
2170
|
`neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
|
|
@@ -2466,7 +2290,7 @@ async function startDaemon(opts = {}) {
|
|
|
2466
2290
|
});
|
|
2467
2291
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
2468
2292
|
});
|
|
2469
|
-
await
|
|
2293
|
+
await fs2.unlink(pidPath).catch(() => {
|
|
2470
2294
|
});
|
|
2471
2295
|
throw new Error(
|
|
2472
2296
|
`neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
|
|
@@ -2501,7 +2325,7 @@ async function startDaemon(opts = {}) {
|
|
|
2501
2325
|
});
|
|
2502
2326
|
if (otlpApp) await otlpApp.close().catch(() => {
|
|
2503
2327
|
});
|
|
2504
|
-
await
|
|
2328
|
+
await fs2.unlink(pidPath).catch(() => {
|
|
2505
2329
|
});
|
|
2506
2330
|
throw new Error(
|
|
2507
2331
|
`neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
|
|
@@ -2548,8 +2372,8 @@ async function startDaemon(opts = {}) {
|
|
|
2548
2372
|
let registryWatcher = null;
|
|
2549
2373
|
let reloadTimer = null;
|
|
2550
2374
|
if (!singleProject) try {
|
|
2551
|
-
const regDir =
|
|
2552
|
-
const regBase =
|
|
2375
|
+
const regDir = path2.dirname(regPath);
|
|
2376
|
+
const regBase = path2.basename(regPath);
|
|
2553
2377
|
registryWatcher = watch(regDir, (_eventType, filename) => {
|
|
2554
2378
|
if (filename !== null && filename !== regBase) return;
|
|
2555
2379
|
if (reloadTimer) clearTimeout(reloadTimer);
|
|
@@ -2599,7 +2423,7 @@ async function startDaemon(opts = {}) {
|
|
|
2599
2423
|
if (daemonRecord) {
|
|
2600
2424
|
await clearDaemonRecord(daemonRecord, home);
|
|
2601
2425
|
}
|
|
2602
|
-
await
|
|
2426
|
+
await fs2.unlink(pidPath).catch(() => {
|
|
2603
2427
|
});
|
|
2604
2428
|
};
|
|
2605
2429
|
return {
|
|
@@ -2617,12 +2441,6 @@ async function startDaemon(opts = {}) {
|
|
|
2617
2441
|
}
|
|
2618
2442
|
|
|
2619
2443
|
export {
|
|
2620
|
-
readConnectorsConfig,
|
|
2621
|
-
isEnvRef,
|
|
2622
|
-
upsertConnectorEntry,
|
|
2623
|
-
removeConnectorEntry,
|
|
2624
|
-
autoSlugConnectorId,
|
|
2625
|
-
describeCredential,
|
|
2626
2444
|
PROVIDER_DISPATCH,
|
|
2627
2445
|
getProviderDispatch,
|
|
2628
2446
|
validateConnectorEntry,
|
|
@@ -2636,4 +2454,4 @@ export {
|
|
|
2636
2454
|
resolveHost,
|
|
2637
2455
|
startDaemon
|
|
2638
2456
|
};
|
|
2639
|
-
//# sourceMappingURL=chunk-
|
|
2457
|
+
//# sourceMappingURL=chunk-V2HUBS5J.js.map
|