@neat.is/core 0.9.21-dev.20260923 → 0.9.21
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-NFJ4W5T6.js → chunk-J3VVEB45.js} +200 -50
- package/dist/chunk-J3VVEB45.js.map +1 -0
- package/dist/{chunk-SK2FEJKB.js → chunk-KVJQQUUY.js} +186 -40
- package/dist/chunk-KVJQQUUY.js.map +1 -0
- package/dist/cli.cjs +494 -345
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +470 -177
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +482 -189
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +225 -78
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-NFJ4W5T6.js.map +0 -1
- package/dist/chunk-SK2FEJKB.js.map +0 -1
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
buildApi,
|
|
8
8
|
extractFromDirectory,
|
|
9
9
|
getGraph,
|
|
10
|
+
getPushProviderDispatch,
|
|
10
11
|
handleSpan,
|
|
11
12
|
isBoundaryTimeoutSymptom,
|
|
12
13
|
junctionFetch,
|
|
@@ -32,7 +33,7 @@ import {
|
|
|
32
33
|
startStalenessLoop,
|
|
33
34
|
touchLastSeen,
|
|
34
35
|
writeAtomically
|
|
35
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-J3VVEB45.js";
|
|
36
37
|
import {
|
|
37
38
|
assertBindAuthority,
|
|
38
39
|
buildOtelReceiver,
|
|
@@ -80,6 +81,10 @@ async function reconcileFrontierSurfaces(graph, errorsPath) {
|
|
|
80
81
|
stageHangSurfaces(graph, incidents);
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
// src/connectors/hosted.ts
|
|
85
|
+
import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
86
|
+
import { dirname, join } from "path";
|
|
87
|
+
|
|
83
88
|
// src/connectors/railway/target-ref.ts
|
|
84
89
|
function nonEmptyString(v) {
|
|
85
90
|
return typeof v === "string" && v.length > 0;
|
|
@@ -124,11 +129,14 @@ function credentialRecord(provider, cred) {
|
|
|
124
129
|
switch (provider) {
|
|
125
130
|
case "supabase":
|
|
126
131
|
return { managementToken: cred.accessToken };
|
|
132
|
+
case "gcp":
|
|
133
|
+
if (!cred.projectRef) throw new Error("gcp credential delivered without a project ref");
|
|
134
|
+
return { projectId: cred.projectRef, accessToken: cred.accessToken };
|
|
127
135
|
default:
|
|
128
136
|
return { token: cred.accessToken };
|
|
129
137
|
}
|
|
130
138
|
}
|
|
131
|
-
function hostedOptions(provider, summary, serviceName) {
|
|
139
|
+
function hostedOptions(provider, summary, serviceName, firebaseServiceMap) {
|
|
132
140
|
switch (provider) {
|
|
133
141
|
case "supabase": {
|
|
134
142
|
const ref = summary.projectRef;
|
|
@@ -144,10 +152,125 @@ function hostedOptions(provider, summary, serviceName) {
|
|
|
144
152
|
serviceNameById: { [target.serviceId]: serviceName }
|
|
145
153
|
};
|
|
146
154
|
}
|
|
155
|
+
case "firebase": {
|
|
156
|
+
return { inferServices: true, excludeCloudRun: true, ...firebaseServiceMap ?? {} };
|
|
157
|
+
}
|
|
158
|
+
case "cloud-run":
|
|
159
|
+
case "gcp-lb":
|
|
160
|
+
return { inferServices: true };
|
|
147
161
|
default:
|
|
148
162
|
return null;
|
|
149
163
|
}
|
|
150
164
|
}
|
|
165
|
+
function parseFirebaseServiceMap(raw) {
|
|
166
|
+
if (!raw) return void 0;
|
|
167
|
+
try {
|
|
168
|
+
const parsed = JSON.parse(raw);
|
|
169
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
|
|
170
|
+
const out = {};
|
|
171
|
+
for (const key of ["functions", "cloudRun", "hosting"]) {
|
|
172
|
+
const group = parsed[key];
|
|
173
|
+
if (group === void 0) continue;
|
|
174
|
+
if (!group || typeof group !== "object" || Array.isArray(group)) return void 0;
|
|
175
|
+
const entries = Object.entries(group);
|
|
176
|
+
if (entries.some(([, v]) => typeof v !== "string" || v.length === 0)) return void 0;
|
|
177
|
+
out[key] = Object.fromEntries(entries);
|
|
178
|
+
}
|
|
179
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
180
|
+
} catch {
|
|
181
|
+
return void 0;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function pushHandlesPath(projectDir) {
|
|
185
|
+
return join(projectDir, "neat-out", "connectors-hosted.json");
|
|
186
|
+
}
|
|
187
|
+
async function readPushHandles(projectDir) {
|
|
188
|
+
try {
|
|
189
|
+
const parsed = JSON.parse(await readFile(pushHandlesPath(projectDir), "utf8"));
|
|
190
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
191
|
+
} catch {
|
|
192
|
+
return {};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function writePushHandle(projectDir, provider, handle) {
|
|
196
|
+
const path5 = pushHandlesPath(projectDir);
|
|
197
|
+
const handles = await readPushHandles(projectDir);
|
|
198
|
+
handles[provider] = handle;
|
|
199
|
+
await mkdir(dirname(path5), { recursive: true });
|
|
200
|
+
const tmp = `${path5}.${process.pid}.tmp`;
|
|
201
|
+
await writeFile(tmp, `${JSON.stringify(handles, null, 2)}
|
|
202
|
+
`, "utf8");
|
|
203
|
+
await rename(tmp, path5);
|
|
204
|
+
}
|
|
205
|
+
function pushCredentialRecord(provider, cred, deps) {
|
|
206
|
+
switch (provider) {
|
|
207
|
+
case "vercel":
|
|
208
|
+
return { token: cred.accessToken, otelToken: deps.otelToken ?? deps.daemonToken };
|
|
209
|
+
default:
|
|
210
|
+
return { token: cred.accessToken };
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function hostedPushOptions(provider, summary, deps) {
|
|
214
|
+
switch (provider) {
|
|
215
|
+
case "vercel": {
|
|
216
|
+
if (!deps.publicUrl) {
|
|
217
|
+
return { skip: "no public URL for this daemon (NEAT_PUBLIC_URL) \u2014 a drain has nowhere to deliver" };
|
|
218
|
+
}
|
|
219
|
+
if (!summary.projectRef) return { skip: "no Vercel team selected yet \u2014 drains are team-scoped" };
|
|
220
|
+
const endpoint = `${deps.publicUrl.replace(/\/+$/, "")}/v1/traces`;
|
|
221
|
+
return { options: { teamId: summary.projectRef, endpoint }, endpoint };
|
|
222
|
+
}
|
|
223
|
+
default:
|
|
224
|
+
return { skip: "no hosted option mapping for this push provider" };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async function provisionHostedPushProvider(summary, dispatch, input) {
|
|
228
|
+
const { deps, projectDir, onSkip, onProvisioned } = input;
|
|
229
|
+
const provider = summary.provider;
|
|
230
|
+
const mapped = hostedPushOptions(provider, summary, deps);
|
|
231
|
+
if ("skip" in mapped) {
|
|
232
|
+
onSkip?.(provider, mapped.skip);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
let cred;
|
|
236
|
+
try {
|
|
237
|
+
cred = await cpGet(
|
|
238
|
+
`/internal/projects/${deps.projectId}/connections/${provider}/credential`,
|
|
239
|
+
deps
|
|
240
|
+
);
|
|
241
|
+
} catch (err) {
|
|
242
|
+
onSkip?.(provider, `credential delivery failed \u2014 ${err.message}`);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const credentials = pushCredentialRecord(provider, cred, deps);
|
|
246
|
+
const existing = (await readPushHandles(projectDir))[provider];
|
|
247
|
+
const alreadyProvisioned = existing !== void 0 && existing.endpoint === mapped.endpoint;
|
|
248
|
+
const options = alreadyProvisioned ? { ...mapped.options, ...existing.options } : mapped.options;
|
|
249
|
+
const seam = deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {};
|
|
250
|
+
const validation = await dispatch.validate({ credentials, options, ...seam });
|
|
251
|
+
if (!validation.ok) {
|
|
252
|
+
onSkip?.(provider, `drain delivery test failed \u2014 ${validation.reason}`);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (alreadyProvisioned) {
|
|
256
|
+
onProvisioned?.(provider, "drain already provisioned \u2014 delivery re-validated");
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const result = await dispatch.provision({ credentials, options, ...seam });
|
|
260
|
+
if (!result.ok) {
|
|
261
|
+
onSkip?.(provider, `drain provisioning failed \u2014 ${result.reason}`);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
await writePushHandle(projectDir, provider, {
|
|
265
|
+
endpoint: mapped.endpoint,
|
|
266
|
+
options: result.options ?? {},
|
|
267
|
+
provisionedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
268
|
+
});
|
|
269
|
+
onProvisioned?.(provider, result.note);
|
|
270
|
+
}
|
|
271
|
+
var HOSTED_CONNECTORS_FOR = {
|
|
272
|
+
gcp: ["firebase", "cloud-run", "gcp-lb"]
|
|
273
|
+
};
|
|
151
274
|
function createHostedCredentialSource(provider, deps) {
|
|
152
275
|
let cached = null;
|
|
153
276
|
return async () => {
|
|
@@ -176,40 +299,50 @@ async function startHostedConnectors(input) {
|
|
|
176
299
|
if (!Array.isArray(connections)) return () => {
|
|
177
300
|
};
|
|
178
301
|
const stops = [];
|
|
302
|
+
const lookupPush = input.pushDispatch ?? getPushProviderDispatch;
|
|
179
303
|
for (const c of connections) {
|
|
180
|
-
const
|
|
181
|
-
if (
|
|
182
|
-
|
|
304
|
+
const push = lookupPush(c.provider);
|
|
305
|
+
if (push) {
|
|
306
|
+
await provisionHostedPushProvider(c, push, input);
|
|
183
307
|
continue;
|
|
184
308
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
309
|
+
const credentialSource = createHostedCredentialSource(c.provider, deps);
|
|
310
|
+
for (const name of HOSTED_CONNECTORS_FOR[c.provider] ?? [c.provider]) {
|
|
311
|
+
const dispatch = PROVIDER_DISPATCH[name];
|
|
312
|
+
if (!dispatch) {
|
|
313
|
+
onSkip?.(name, "no pull connector for this provider");
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (c.needsProjectSelection || !c.projectRef) {
|
|
317
|
+
onSkip?.(name, "no project selected yet \u2014 not pullable");
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
const options = hostedOptions(name, c, project, input.firebaseServiceMap);
|
|
321
|
+
if (!options) {
|
|
322
|
+
onSkip?.(name, "no hosted option mapping for this provider");
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
let built;
|
|
326
|
+
try {
|
|
327
|
+
built = dispatch.build(graph, options);
|
|
328
|
+
} catch (err) {
|
|
329
|
+
onSkip?.(name, err.message);
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
stops.push(
|
|
333
|
+
startLoop(
|
|
334
|
+
built.connector,
|
|
335
|
+
{ projectDir, project, credentials: {}, ...errorsPath ? { errorsPath } : {} },
|
|
336
|
+
graph,
|
|
337
|
+
built.resolveTarget,
|
|
338
|
+
{
|
|
339
|
+
connectorId: `hosted:${name}`,
|
|
340
|
+
// The credential is fetched by the CONNECTION's provider (`gcp`), not the connector's name.
|
|
341
|
+
refreshCredentials: credentialSource
|
|
342
|
+
}
|
|
343
|
+
)
|
|
344
|
+
);
|
|
200
345
|
}
|
|
201
|
-
stops.push(
|
|
202
|
-
startLoop(
|
|
203
|
-
built.connector,
|
|
204
|
-
{ projectDir, project, credentials: {}, ...errorsPath ? { errorsPath } : {} },
|
|
205
|
-
graph,
|
|
206
|
-
built.resolveTarget,
|
|
207
|
-
{
|
|
208
|
-
connectorId: `hosted:${c.provider}`,
|
|
209
|
-
refreshCredentials: createHostedCredentialSource(c.provider, deps)
|
|
210
|
-
}
|
|
211
|
-
)
|
|
212
|
-
);
|
|
213
346
|
}
|
|
214
347
|
return () => {
|
|
215
348
|
for (const stop of stops) stop();
|
|
@@ -223,10 +356,18 @@ async function maybeStartHostedConnectors(input) {
|
|
|
223
356
|
if (!cpUrl || !projectId || !daemonToken) return () => {
|
|
224
357
|
};
|
|
225
358
|
return startHostedConnectors({
|
|
226
|
-
deps: {
|
|
359
|
+
deps: {
|
|
360
|
+
cpUrl,
|
|
361
|
+
projectId,
|
|
362
|
+
daemonToken,
|
|
363
|
+
...env.NEAT_PUBLIC_URL ? { publicUrl: env.NEAT_PUBLIC_URL } : {},
|
|
364
|
+
...env.NEAT_OTEL_TOKEN ? { otelToken: env.NEAT_OTEL_TOKEN } : {},
|
|
365
|
+
...input.fetchImpl ? { fetchImpl: input.fetchImpl } : {}
|
|
366
|
+
},
|
|
227
367
|
graph: input.graph,
|
|
228
368
|
projectDir: input.projectDir,
|
|
229
369
|
project: input.project,
|
|
370
|
+
...parseFirebaseServiceMap(env.NEAT_FIREBASE_SERVICE_MAP) ? { firebaseServiceMap: parseFirebaseServiceMap(env.NEAT_FIREBASE_SERVICE_MAP) } : {},
|
|
230
371
|
...input.errorsPath ? { errorsPath: input.errorsPath } : {},
|
|
231
372
|
...input.onSkip ? { onSkip: input.onSkip } : {}
|
|
232
373
|
});
|
|
@@ -306,6 +447,8 @@ async function syncOneRepo(r, input) {
|
|
|
306
447
|
let dir;
|
|
307
448
|
try {
|
|
308
449
|
dir = await mkdtemp(path.join(tmpRoot, "neat-repo-"));
|
|
450
|
+
await cpPostStatus(deps, r.owner, r.name, { syncStatus: "syncing", detail: "cloning" }).catch(() => {
|
|
451
|
+
});
|
|
309
452
|
await cloneRepo(r.cloneUrl, r.defaultBranch, dir);
|
|
310
453
|
const extracted = await extract(graph, dir);
|
|
311
454
|
const nodes = extracted?.nodesAdded ?? 0;
|
|
@@ -333,23 +476,26 @@ async function runRepoSyncPass(input) {
|
|
|
333
476
|
repos = await cpGet2(`/internal/projects/${input.deps.projectId}/repos`, input.deps);
|
|
334
477
|
} catch (err) {
|
|
335
478
|
input.onSkip?.("(all)", `control plane repo list unreadable \u2014 ${err.message}`);
|
|
336
|
-
return;
|
|
479
|
+
return false;
|
|
337
480
|
}
|
|
338
|
-
if (!Array.isArray(repos)) return;
|
|
481
|
+
if (!Array.isArray(repos)) return false;
|
|
339
482
|
for (const r of repos) {
|
|
340
|
-
if (!needsSync(r)) continue;
|
|
483
|
+
if (!input.forceResync && !needsSync(r)) continue;
|
|
341
484
|
await syncOneRepo(r, input);
|
|
342
485
|
}
|
|
486
|
+
return true;
|
|
343
487
|
}
|
|
344
488
|
async function startRepoSync(input) {
|
|
345
489
|
const intervalMs = input.intervalMs ?? DEFAULT_SYNC_INTERVAL_MS;
|
|
346
490
|
let stopped = false;
|
|
347
491
|
let running = false;
|
|
492
|
+
let bootResyncDone = false;
|
|
348
493
|
const tick = async () => {
|
|
349
494
|
if (stopped || running) return;
|
|
350
495
|
running = true;
|
|
351
496
|
try {
|
|
352
|
-
await runRepoSyncPass(input);
|
|
497
|
+
const listed = await runRepoSyncPass({ ...input, forceResync: !bootResyncDone });
|
|
498
|
+
if (listed) bootResyncDone = true;
|
|
353
499
|
} finally {
|
|
354
500
|
running = false;
|
|
355
501
|
}
|
|
@@ -740,7 +886,7 @@ function createK8sResolveTarget(graph) {
|
|
|
740
886
|
}
|
|
741
887
|
|
|
742
888
|
// src/connectors/kubernetes/substrate.ts
|
|
743
|
-
import { readFile } from "fs/promises";
|
|
889
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
744
890
|
import os2 from "os";
|
|
745
891
|
import path2 from "path";
|
|
746
892
|
function defaultHome() {
|
|
@@ -754,7 +900,7 @@ function k8sSubstrateConfigPath(home = defaultHome()) {
|
|
|
754
900
|
async function readK8sSubstrateConfig(home = defaultHome()) {
|
|
755
901
|
let raw;
|
|
756
902
|
try {
|
|
757
|
-
raw = await
|
|
903
|
+
raw = await readFile2(k8sSubstrateConfigPath(home), "utf8");
|
|
758
904
|
} catch {
|
|
759
905
|
return { version: 1, deployments: [] };
|
|
760
906
|
}
|
|
@@ -1753,4 +1899,4 @@ export {
|
|
|
1753
1899
|
resolveHost,
|
|
1754
1900
|
startDaemon
|
|
1755
1901
|
};
|
|
1756
|
-
//# sourceMappingURL=chunk-
|
|
1902
|
+
//# sourceMappingURL=chunk-KVJQQUUY.js.map
|