@haystackeditor/cli 0.25.0 → 0.25.1

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.
@@ -359,18 +359,26 @@ export async function initCommand(options) {
359
359
  const plan = planInit(gitRoot);
360
360
  // Telemetry is part of onboarding (CAPTURE-V1 rule 8): planned on every run; a missing answer stops only it.
361
361
  const telemetryModule = await import('./init-telemetry.js');
362
- const telemetryOptions = { app: options.app, origins: options.origin ?? [], consent: options.consent };
362
+ const telemetryOptions = { app: options.app, origins: options.origin ?? [], consent: options.consent, urlRewrites: options.urlRewrites };
363
363
  let telemetryPlan = await telemetryModule.planTelemetry(gitRoot, repository, telemetryOptions);
364
364
  const telemetryFrom = plan.changes.length;
365
365
  plan.changes.push(...telemetryPlan.changes);
366
366
  plan.notices.push(...telemetryPlan.notices);
367
367
  let notesWritten = null;
368
368
  let telemetry = null;
369
+ // Before the apply (a preview, or no login), the report is the preview: what telemetry will do, `received` unchecked (null).
370
+ const report = () => telemetry ?? telemetryModule.previewTelemetry(telemetryPlan, repository, notes);
371
+ const showPreview = () => {
372
+ const preview = telemetryModule.previewTelemetry(telemetryPlan, repository, notes);
373
+ for (const line of telemetryModule.formatTelemetry(preview.telemetry, preview.steps))
374
+ say(line);
375
+ say('');
376
+ };
369
377
  const finish = (status, onboarding, exitCode) => {
370
378
  if (options.json) {
371
379
  const changes = plan.changes.map(({ path, action, reason, diff }) => ({ path, action, reason, diff }));
372
380
  process.stdout.write(`${JSON.stringify(withSchema('init', { status, repository, changes, notices: plan.notices, notes: notesWritten,
373
- onboarding, telemetry: telemetry?.telemetry ?? null, telemetrySteps: telemetry?.steps ?? [] }), null, 2)}\n`);
381
+ onboarding, telemetry: report().telemetry, telemetrySteps: report().steps }), null, 2)}\n`);
374
382
  }
375
383
  process.exitCode = exitCode;
376
384
  };
@@ -397,6 +405,7 @@ export async function initCommand(options) {
397
405
  catch (error) {
398
406
  if (!(error instanceof NotLoggedInError))
399
407
  throw error;
408
+ showPreview();
400
409
  say(chalk.yellow('Not logged in, so nothing was changed. Run `haystack login`, then `haystack init` again.'));
401
410
  finish('login-required', null, EXIT_CODES['login-required']);
402
411
  return;
@@ -404,6 +413,7 @@ export async function initCommand(options) {
404
413
  if (plan.changes.length > 0 && !options.yes) {
405
414
  const interactive = !options.json && process.stdin.isTTY === true && process.stdout.isTTY === true;
406
415
  if (!interactive || !await confirmed()) {
416
+ showPreview();
407
417
  say('Nothing was changed. Run `haystack init --yes` to make these changes.');
408
418
  finish('planned', null, EXIT_CODES.planned);
409
419
  return;
@@ -103,6 +103,12 @@ export function parseOnboardingStatus(value, expected) {
103
103
  if (!Array.isArray(value.services) || !value.services.every(service => isRecord(service) && typeof service.id === 'string'
104
104
  && SERVICE_ENGINES.includes(service.engine)))
105
105
  invalid('its services');
106
+ const review = value.review;
107
+ if (!(review === null || (isRecord(review) && (review.verdict === 'accept'
108
+ || (review.verdict === 'skipped' && typeof review.reason === 'string' && review.reason.length > 0)))))
109
+ invalid('its review');
110
+ if (value.state !== 'ready' && review !== null)
111
+ invalid('its review');
106
112
  if (value.block !== null)
107
113
  parseOnboardingBlock(value.block);
108
114
  const checkpoint = value.checkpoint;
@@ -311,8 +317,18 @@ export function formatOnboarding(status) {
311
317
  const outside = [...status.standIns, ...status.unsetProviders, ...standInGapNotes(status.standInGaps)];
312
318
  if (outside.length)
313
319
  lines.push('', ...outside.map(note => chalk.yellow(`Note: ${safe(note)}`)));
320
+ const unreviewed = reviewNotes(status.review);
321
+ if (unreviewed.length)
322
+ lines.push('', ...unreviewed.map(note => chalk.yellow(`Note: ${safe(note)}`)));
314
323
  return lines.join('\n');
315
324
  }
325
+ /** Rule 17 (amendment 32): an onboarding the independent review skipped, as the sentence every result shows; none otherwise. */
326
+ export function reviewNotes(review) {
327
+ if (review?.verdict !== 'skipped')
328
+ return [];
329
+ return [`This onboarding was NOT reviewed: the independent review that checks the setup did not cheat is turned off for this `
330
+ + `team (${review.reason}).`];
331
+ }
316
332
  /** Rule 15(g), amendment 3: a stand-in gap as the sentence every result shows. */
317
333
  export function standInGapNotes(gaps) {
318
334
  return gaps.map(gap => `A stand-in could not serve ${gap} while onboarding signed in and did the workflow; the crawl may meet it too.`);
@@ -31,7 +31,7 @@ import { classifyHttpError, readWithRetries, SERVICE_SILENCE_LIMIT_MS, ServiceSi
31
31
  import { GATEWAY_TIMEOUT_MS, gatewayFetch } from './case-batch.js';
32
32
  import { CRAWL_BRIEF_MAX_FUNCTIONS, CRAWL_BRIEF_MAX_GROUPS, CRAWL_BRIEF_MAX_PRODUCTION_ROWS, CRAWL_BUDGET_MAX_MS, CRAWL_BUDGET_MIN_MS, CRAWL_MAX_FINDINGS, CRAWL_MAX_FINDING_STEPS, CRAWL_MAX_IDEAS, CRAWL_MAX_STAND_IN_LINES, CRAWL_MAX_STAND_IN_ROWS, CRAWL_MAX_TEXT_CHARS, CRAWL_WAIT_MAX_MS, CRAWL_POOLS, } from './crawl-contract.js';
33
33
  import { captureCheckout, crawlUnavailableText, EXPLICIT_WALL_MS, normalModeFailure, postPrecomputeCapture, } from './verify-precompute.js';
34
- import { formatOnboarding, onboardingExitCode, readOnboardingStatus, reportOnboardingState, standInGapNotes, waitForOnboarding } from './verify-onboarding.js';
34
+ import { formatOnboarding, onboardingExitCode, readOnboardingStatus, reportOnboardingState, reviewNotes, standInGapNotes, waitForOnboarding, } from './verify-onboarding.js';
35
35
  import { formatCapture, readPreVerifyCapture } from './capture-brief.js';
36
36
  const CRAWLS_PATH = '/api/agent/cloud-verifier/crawls';
37
37
  /** A held read (waitAfter): the service's hold, then the time any read gets. */
@@ -1094,7 +1094,7 @@ export async function verifyCommand(options) {
1094
1094
  else {
1095
1095
  console.log(formatCrawl(view));
1096
1096
  const notes = [...(onboarding?.dataAbsences ?? []), ...(onboarding?.versionChoices ?? []), ...(onboarding?.standIns ?? []),
1097
- ...(onboarding?.unsetProviders ?? []), ...standInGapNotes(onboarding?.standInGaps ?? [])];
1097
+ ...(onboarding?.unsetProviders ?? []), ...standInGapNotes(onboarding?.standInGaps ?? []), ...reviewNotes(onboarding?.review ?? null)];
1098
1098
  if (notes.length)
1099
1099
  console.log(['', ...notes.map(note => `Note: ${safe(note)}`)].join('\n'));
1100
1100
  }
package/dist/index.js CHANGED
@@ -154,6 +154,7 @@ program
154
154
  .option('--app <dir>', 'The app whose telemetry init sets up, when the repository has several; see below')
155
155
  .option('--origin <url>', 'A production origin of the app (repeat for several): browser capture accepts events only from these', (value, previous = []) => [...previous, value])
156
156
  .option('--consent <choice>', 'How the site asks visitors for consent: its consent platform, or not-required when your user decided so')
157
+ .option('--url-rewrites <answer>', 'When init asks: none, or hidden-segment:<name> when the app\'s middleware adds a leading [<name>] segment to page paths')
157
158
  .option('--json', 'The result as one JSON document on stdout (see `haystack schema init`)')
158
159
  .addHelpText('after', `
159
160
  There is no setup file: Haystack works out how to run the app from what the
@@ -1045,6 +1046,7 @@ program.command('capture').description('Real users\' journeys and devices: the t
1045
1046
  .description('Publish the app\'s route manifest into its static output (the build step `haystack init` adds)')
1046
1047
  .option('--app <dir>', 'The app directory (default: the current directory)')
1047
1048
  .option('--rails-routes <file>', 'Rails: the routes file the rake task wrote')
1049
+ .option('--adapter <id>', 'The framework adapter to use instead of the one init recorded in .haystack/capture.json')
1048
1050
  .option('--json', 'The release, counts and files written as JSON')
1049
1051
  .action((options) => runPublicCommand(() => captureManifestCommand(options), options.json));
1050
1052
  // ─── db ──────────────────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haystackeditor/cli",
3
- "version": "0.25.0",
3
+ "version": "0.25.1",
4
4
  "description": "haystack verify: run your app with and without a change, and see what the change broke",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://docs.haystackeditor.com/cli/schemas/init.v1.json",
4
4
  "title": "Haystack init: this repository's setup for `haystack verify`",
5
- "description": "Machine-readable output of `haystack init --json`. `changes` are the project-local changes init planned (made when status is done); `onboarding` is the base's OnboardingStatusV1 (onboarding_contracts.ts, APP-ONBOARDING-V1) as POST /api/agent/cloud-verifier/onboarding returns it, null unless status is done. `notes` says what became of `--notes`: \"written\" (the repository's onboarding notes are now these), \"unchanged\" (they already were), null without `--notes` or when init stopped before writing them. Exit codes: 0 done (onboarding running or ready), 1 failed (the error envelope), 2 planned (nothing written; run with --yes), 3 done but onboarding blocked, 4 login-required, 5 app-required (install the Haystack GitHub App), 6 done but onboarding stopped before finishing (run init again). `telemetry` (CAPTURE-V1 rule 8e, InitTelemetryReport in capture_contracts.ts) is null unless status is done: per part (`capture`, the browser tag; `server`, server telemetry) `installed`, `manual` with the exact `steps`, or `unsupported` with `why`, and `received`, the newest telemetry Haystack holds for this application (null when none; `stale` past 24 hours), plus `dbProfileCommand`, the `haystack db profile` command for the user to run with production read access (null when the app's database is not one it profiles). `telemetrySteps` is what the person does next for telemetry to arrive, in order, including the off switches; [] without telemetry.",
5
+ "description": "Machine-readable output of `haystack init --json`. `changes` are the project-local changes init planned (made when status is done); `onboarding` is the base's OnboardingStatusV1 (onboarding_contracts.ts, APP-ONBOARDING-V1) as POST /api/agent/cloud-verifier/onboarding returns it, null unless status is done. `notes` says what became of `--notes`: \"written\" (the repository's onboarding notes are now these), \"unchanged\" (they already were), null without `--notes` or when init stopped before writing them. Exit codes: 0 done (onboarding running or ready), 1 failed (the error envelope), 2 planned (nothing written; run with --yes), 3 done but onboarding blocked, 4 login-required, 5 app-required (install the Haystack GitHub App), 6 done but onboarding stopped before finishing (run init again). `telemetry` (CAPTURE-V1 rule 8e, InitTelemetryReport in capture_contracts.ts) is what telemetry does (status done) or will do (planned, login-required: the same report before anything is written, every `received` null because nothing was checked): per part (`capture`, the browser tag; `server`, server telemetry) `installed`, `manual` with the exact `steps`, or `unsupported` with `why`, and `received`, the newest telemetry Haystack holds for this application (null when none; `stale` past 24 hours), plus `dbProfileCommand`, the `haystack db profile` command for the user to run with production read access (null when the app's database is not one it profiles). `telemetrySteps` is what the person does next for telemetry to arrive, in order, including the off switches; [] without telemetry.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "required": ["schema_version", "status", "repository", "changes", "notices", "notes", "onboarding", "telemetry", "telemetrySteps"],