@neat.is/core 0.10.2 → 0.10.3

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/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  resolveNeatVersion,
9
9
  startK8sSubstratePolling,
10
10
  writeDaemonRecord
11
- } from "./chunk-CAGAHYDB.js";
11
+ } from "./chunk-DLQNMU3R.js";
12
12
  import {
13
13
  buildSearchIndex
14
14
  } from "./chunk-BC53SCT7.js";
@@ -82,7 +82,7 @@ import {
82
82
  startStalenessLoop,
83
83
  upsertConnectorEntry,
84
84
  validateConnectorEntry
85
- } from "./chunk-TXKOAART.js";
85
+ } from "./chunk-LNIVRE4L.js";
86
86
  import {
87
87
  startOtelGrpcReceiver
88
88
  } from "./chunk-5SJHA5ZH.js";
@@ -4208,9 +4208,15 @@ async function snapshotProjectStatus(restPort, project, body) {
4208
4208
  }
4209
4209
  return [{ name: project, status: await probeProjectHealth(restPort, project) }];
4210
4210
  }
4211
- async function waitForDaemonReady(restPort, project, timeoutMs) {
4212
- const deadline = Date.now() + timeoutMs;
4213
- let lastBootstrapping = [];
4211
+ var STILL_WAITING_INTERVAL_MS = 1e4;
4212
+ function daemonProgressLine(project, event) {
4213
+ if (event.kind === "starting") return `starting the daemon for ${project}\u2026`;
4214
+ return `still waiting for ${project}'s daemon (${Math.round(event.elapsedMs / 1e3)}s)`;
4215
+ }
4216
+ async function waitForDaemonReady(restPort, project, timeoutMs, onProgress, reminderIntervalMs = STILL_WAITING_INTERVAL_MS) {
4217
+ const startedAt = Date.now();
4218
+ const deadline = startedAt + timeoutMs;
4219
+ let nextReminderAt = startedAt + reminderIntervalMs;
4214
4220
  while (Date.now() < deadline) {
4215
4221
  const body = await fetchDaemonHealth(restPort);
4216
4222
  if (body !== null) {
@@ -4220,15 +4226,10 @@ async function waitForDaemonReady(restPort, project, timeoutMs) {
4220
4226
  if (bootstrapping.length === 0) {
4221
4227
  return { ready: true, brokenProjects: broken, stillBootstrapping: [] };
4222
4228
  }
4223
- const key = bootstrapping.slice().sort().join(",");
4224
- const prevKey = lastBootstrapping.slice().sort().join(",");
4225
- if (key !== prevKey) {
4226
- const plural = bootstrapping.length === 1 ? "" : "s";
4227
- console.log(
4228
- `neat: waiting on ${bootstrapping.length} project${plural}: ${bootstrapping.join(", ")}`
4229
- );
4230
- lastBootstrapping = bootstrapping;
4231
- }
4229
+ }
4230
+ if (Date.now() >= nextReminderAt) {
4231
+ onProgress?.({ kind: "waiting", elapsedMs: Date.now() - startedAt });
4232
+ nextReminderAt += reminderIntervalMs;
4232
4233
  }
4233
4234
  await new Promise((r) => setTimeout(r, PROBE_INTERVAL_MS));
4234
4235
  }
@@ -4440,7 +4441,8 @@ async function ensureProjectDaemon(opts) {
4440
4441
  projectPath: opts.projectPath,
4441
4442
  ports: allocated
4442
4443
  });
4443
- const ready = await waitForDaemonReady(allocated.rest, opts.project, timeoutMs);
4444
+ opts.onProgress?.({ kind: "starting" });
4445
+ const ready = await waitForDaemonReady(allocated.rest, opts.project, timeoutMs, opts.onProgress);
4444
4446
  if (!ready.ready) {
4445
4447
  return {
4446
4448
  status: "timed-out",
@@ -4586,7 +4588,8 @@ async function runOrchestrator(opts) {
4586
4588
  const outcome = await ensureProjectDaemon({
4587
4589
  project: currentProjectName,
4588
4590
  projectPath: opts.scanPath,
4589
- timeoutMs
4591
+ timeoutMs,
4592
+ onProgress: (event) => console.log(`neat: ${daemonProgressLine(currentProjectName, event)}`)
4590
4593
  });
4591
4594
  const allocated = "ports" in outcome ? outcome.ports : null;
4592
4595
  switch (outcome.status) {
@@ -5444,6 +5447,15 @@ function bindUrl(webUrl, projectId) {
5444
5447
  function onboardingUrl(webUrl) {
5445
5448
  return `${webUrl}/onboarding`;
5446
5449
  }
5450
+ async function offerLocalExtraction(input, d, out, readLine) {
5451
+ const run = d.orchestrator;
5452
+ if (!run || !readLine) return false;
5453
+ const answer = (await readLine("Build it now? [Y/n] "))?.trim().toLowerCase();
5454
+ if (answer === "n" || answer === "no") return false;
5455
+ out("");
5456
+ const code = await run(d.cwd ?? process.cwd(), {});
5457
+ return code === 0;
5458
+ }
5447
5459
  async function runPostLoginConnect(input) {
5448
5460
  const d = input.deps;
5449
5461
  const out = d.out ?? ((line) => console.log(line));
@@ -5453,9 +5465,27 @@ async function runPostLoginConnect(input) {
5453
5465
  const readLine = d.readLine;
5454
5466
  const repo = await detect6(cwd);
5455
5467
  out("");
5468
+ const cmd = commandPrefix();
5469
+ const syncLine = `${cmd} sync --to ${input.project.name}`;
5456
5470
  if (!repo) {
5457
5471
  out("This directory has no GitHub remote, so it can't be bound to the project.");
5458
- out(` Push its graph instead: neat sync --to ${input.project.name}`);
5472
+ const hasGraph = await (d.hasLocalGraph ?? hasLocalSnapshot)(cwd);
5473
+ if (!hasGraph) {
5474
+ out(`A graph of this directory has to be built first, then pushed to ${input.project.name}.`);
5475
+ const built = await offerLocalExtraction(input, d, out, readLine);
5476
+ out("");
5477
+ if (built) {
5478
+ out(`Now push it to ${input.project.name}:`);
5479
+ out(` ${syncLine}`);
5480
+ } else {
5481
+ out(`When you're ready, build it and push it to ${input.project.name}:`);
5482
+ out(` ${cmd}`);
5483
+ out(` ${syncLine}`);
5484
+ }
5485
+ out(` Manage projects and repos: ${input.webUrl}`);
5486
+ return;
5487
+ }
5488
+ out(` Push its graph instead: ${syncLine}`);
5459
5489
  out(` Manage projects and repos: ${input.webUrl}`);
5460
5490
  return;
5461
5491
  }
@@ -5489,18 +5519,18 @@ async function runPostLoginConnect(input) {
5489
5519
  out("");
5490
5520
  if (!hasGraph) {
5491
5521
  out("There is no local graph to push yet. Build one, then push it:");
5492
- out(" neat");
5493
- out(` neat sync --to ${input.project.name}`);
5522
+ out(` ${cmd}`);
5523
+ out(` ${syncLine}`);
5494
5524
  return;
5495
5525
  }
5496
5526
  out("Push the local graph with:");
5497
- out(` neat sync --to ${input.project.name}`);
5527
+ out(` ${syncLine}`);
5498
5528
  return;
5499
5529
  }
5500
5530
  out("");
5501
5531
  out("No problem \u2014 when you want this repo in the graph:");
5502
5532
  out(` Bind it: ${bindUrl(input.webUrl, input.project.id)}`);
5503
- out(` Or push it: neat sync --to ${input.project.name}`);
5533
+ out(` Or push it: ${syncLine}`);
5504
5534
  }
5505
5535
 
5506
5536
  // src/login-sso.ts
@@ -5679,7 +5709,7 @@ async function runSsoLogin(opts, deps) {
5679
5709
  ...deps.signal ? { signal: deps.signal } : {}
5680
5710
  });
5681
5711
  if (waited.kind === "aborted") {
5682
- out("Stopped. Run `neat login` again once your project is up.");
5712
+ out(`Stopped. Run \`${commandPrefix()} login\` again once your project is up.`);
5683
5713
  return 130;
5684
5714
  }
5685
5715
  if (waited.kind === "timeout") {
@@ -5747,7 +5777,7 @@ async function runSsoLogin(opts, deps) {
5747
5777
  }
5748
5778
  });
5749
5779
  out("");
5750
- out("Run `neat logout` to switch back to your local daemon.");
5780
+ out(`Run \`${commandPrefix()} logout\` to switch back to your local daemon.`);
5751
5781
  }
5752
5782
  return 0;
5753
5783
  }
@@ -6646,7 +6676,13 @@ async function runUpCommand(argv, deps) {
6646
6676
  err("neat up: that daemon doesn't run on this machine, so it isn't yours to start.");
6647
6677
  return 2;
6648
6678
  }
6649
- const outcome = await ensure({ project: entry2.name, projectPath: entry2.path });
6679
+ const project = entry2.name;
6680
+ const outcome = await ensure({
6681
+ project,
6682
+ projectPath: entry2.path,
6683
+ // `--json` prints one object and nothing else, so progress stays quiet there.
6684
+ ...args.json ? {} : { onProgress: (event) => out(`neat up: ${daemonProgressLine(project, event)}`) }
6685
+ });
6650
6686
  const log = daemonLogPath(entry2.path);
6651
6687
  if (args.json) {
6652
6688
  const ports = "ports" in outcome ? outcome.ports : void 0;
@@ -9757,23 +9793,24 @@ async function tryOrchestrator(cmd, parsed, overrides = {}) {
9757
9793
  return result.exitCode;
9758
9794
  }
9759
9795
  async function runWelcomeFlow(parsed) {
9796
+ const runLocal = async (cwd, opts) => await tryOrchestrator(
9797
+ cwd,
9798
+ {
9799
+ ...parsed,
9800
+ ...opts?.project !== void 0 ? { project: opts.project } : {},
9801
+ ...opts?.noInstrument !== void 0 ? { noInstrument: opts.noInstrument } : {},
9802
+ ...opts?.yes !== void 0 ? { yes: opts.yes } : {}
9803
+ },
9804
+ { skipBanner: opts?.headerShown === true }
9805
+ ) ?? 0;
9760
9806
  return runWelcome({
9761
- login: (loginArgv) => runLoginCommand(loginArgv),
9807
+ login: (loginArgv) => runLoginCommand(loginArgv, { connect: { orchestrator: runLocal } }),
9762
9808
  // What the person chose at the door reaches the orchestrator as the flags they
9763
9809
  // stand for: the name as `--project`, the instrument answer as `--no-instrument`
9764
9810
  // or as `yes` (which keeps the orchestrator's own prompt quiet, since the door
9765
9811
  // already asked). `headerShown` isn't a flag anyone can type — it says the
9766
9812
  // door's wordmark already opened this run, so the orchestrator's banner is skipped.
9767
- orchestrator: async (cwd, opts) => await tryOrchestrator(
9768
- cwd,
9769
- {
9770
- ...parsed,
9771
- ...opts?.project !== void 0 ? { project: opts.project } : {},
9772
- ...opts?.noInstrument !== void 0 ? { noInstrument: opts.noInstrument } : {},
9773
- ...opts?.yes !== void 0 ? { yes: opts.yes } : {}
9774
- },
9775
- { skipBanner: opts?.headerShown === true }
9776
- ) ?? 0,
9813
+ orchestrator: runLocal,
9777
9814
  // An explicit flag is an answer; don't ask again.
9778
9815
  instrumentFlagGiven: parsed.noInstrument || parsed.dryRun
9779
9816
  });