@impeccable/reviews 0.1.6 → 0.1.8

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.
Files changed (41) hide show
  1. package/README.md +22 -20
  2. package/dist/agent.js +9 -4
  3. package/dist/cloudflare-access.js +77 -47
  4. package/dist/cloudflare.js +347 -23
  5. package/dist/fence.js +939 -143
  6. package/dist/focus.js +5 -4
  7. package/dist/host-previews.js +123 -0
  8. package/dist/host-questions.js +101 -21
  9. package/dist/hosted.js +72 -21
  10. package/dist/http.js +2 -0
  11. package/dist/log.js +7 -1
  12. package/dist/login.js +45 -13
  13. package/dist/main.js +37 -14
  14. package/dist/netlify-password.js +8 -6
  15. package/dist/netlify.js +58 -16
  16. package/dist/pi-paths.js +69 -0
  17. package/dist/playbook/cases/cloudflare-previews.md +12 -4
  18. package/dist/playbook/cases/first-preview-failure.md +1 -1
  19. package/dist/playbook/cases/live-app-address.md +15 -0
  20. package/dist/playbook/cases/monorepo.md +8 -8
  21. package/dist/playbook/cases/netlify-previews.md +3 -1
  22. package/dist/playbook/cases/netlify-setup.md +7 -6
  23. package/dist/playbook/cases/preview-protection.md +3 -2
  24. package/dist/playbook/cases/pull-requests.md +3 -3
  25. package/dist/playbook/cases/static-site.md +2 -2
  26. package/dist/playbook/cases/vercel-previews.md +3 -1
  27. package/dist/playbook/cases/vercel-setup.md +9 -5
  28. package/dist/playbook.js +3 -3
  29. package/dist/preflight.js +43 -8
  30. package/dist/report.js +1 -0
  31. package/dist/sandbox.js +364 -50
  32. package/dist/scan.js +134 -0
  33. package/dist/sync.js +44 -7
  34. package/dist/tools.js +682 -140
  35. package/dist/ui/bridge.js +18 -4
  36. package/dist/ui/look.js +1 -1
  37. package/dist/ui/screens.js +5 -3
  38. package/dist/ui/sensitive.js +10 -0
  39. package/dist/vercel-bypass.js +21 -12
  40. package/dist/vercel.js +147 -17
  41. package/package.json +1 -1
@@ -1,23 +1,23 @@
1
1
  ### When this applies
2
2
 
3
- The state's repository has an `apps` item: the scan proposed several apps, folders that each draw pages of their own, and the rest of the setup is asked only once the apps are answered. The scan proposes them, so while the scan item's `scan` is `running` the item may not be there yet: read the state again before taking the repository for one app. The person is asked once, this question, and nothing else about apps. These steps come before the playbook's step 3; steps 3 to 6 then go app by app.
3
+ The state's repository has an `apps` item: the scan proposed several apps, folders that each draw pages of their own, and the rest of the setup is asked only once the apps are answered. The scan proposes them, so while the scan item's `scan` is `running` the item may not be there yet: read the state again before taking the repository for one app. The person is asked once a run, this question, and nothing else about apps: **every run asks it**, however the setup page or an earlier run answered, since a run sets up the apps picked in it; as "The aim" says, it is the one question about apps. These steps come before the playbook's step 3, under the Apps task; steps 3 to 6 then go app by app.
4
4
 
5
5
  ### Steps
6
6
 
7
- 1. **Confirm the proposal** as it stands, while the item's status is `needs-you`: impeccable_write to `apps` with the `confirm` action the item's instructions give. Its answer lists every app of the repository, on and off, with its `key`, `name`, `pathPrefixes` and `enabled`; the one the scan switched on is the repository's default. Done when it answered `apps`.
8
- - **An item that is `done` or `skipped`** was answered before, on the setup page or by an earlier run (a run that stopped at the question left the scan's default, as a skip does): write nothing and ask nothing; the state's apps that are `enabled` are the chosen ones. Step 4.
9
- 2. **Ask which apps get previews**, once, with impeccable_ask: `id: "apps"`, `kind: "multi"`, `title` *"This repository has several apps"*, `prompt` *"Which apps should get Impeccable previews?"*, `hint` *"Each draws pages of its own; untick any that should not get previews."*, and `options` one per app the answer listed: `value` its `key`, `label` its `name` and folder (*"Storefront · apps/storefront"*), `selected: true` on every one. The repository's own app (`main`) is among them only where the Worker kept it as a product of its own, with pages outside every app's folder: its label is its name and `/`. Done when it answered `values`, or a skip.
10
- 3. **Write the picks**, one action a call as the item's instructions give them: `enable` each chosen app that is off, then `disable` each app that is on and was not chosen. The last app that is on cannot be switched off (409), which is why the enables come first. **After Skip**, write nothing more: the app the scan switched on stays the one that is on. Done when the last answer's `apps` show `enabled` true for every chosen app and false for the rest, or, after Skip, the confirm's answer stands.
7
+ 1. **Confirm the proposal** as it stands, while the item's status is `needs-you`: impeccable_write to `apps` with the `confirm` action the item's instructions give. Its answer lists every app of the repository, on and off, with its `key`, `name`, `pathPrefixes` and `enabled`; the one the scan switched on is the repository's default. An item that is `done` or `skipped` was answered before (on the setup page, or by an earlier run): no confirm, and step 2 all the same. Done when it answered `apps`, or the item was answered before.
8
+ 2. **Ask which apps this run sets up**, on every run, with impeccable_ask: `id: "apps"`, `kind: "multi"`, and the `prompt` *"Which apps should get Impeccable previews?"*. The tool words the question and lists the state's apps (the repository's own, `main`, only where the Worker kept it as a product of its own), each picked as it is on, or every one after this run's confirm. Mark the Apps task in progress first. Every write and host tool for an app is refused until it is answered. Done when it answered `values`, `named` (the command was started for that one app with `--app`, so no screen showed), or a skip, whose `on` lists the apps that are on: the ones this run sets up.
9
+ 3. **Write the picks**, one action a call as the item's instructions give them: `enable` each chosen app that is off, then `disable` each app that is on and was not chosen. The last app that is on cannot be switched off (409), which is why the enables come first. **After `named`**, `enable` that app when it is off, and switch no other off. **After Skip**, write nothing more: the apps that are on stay on. Done when the last answer's `apps` show `enabled` true for every chosen app and false for the rest (after `named`, true for that app), or, after Skip, nothing was written.
11
10
  4. **Read the state again** with impeccable_state. Done when the preview item's `apps` names each chosen app's `directory`, `build` and `method` (none yet for an app not set up), and `perApp` its status.
12
- 5. **Set each chosen app up**, one after the other, as the playbook's steps 3 and 4 say for that app alone, `appKey` on every write and check (`preview`, `test-accounts`, `latest-pull-check`, `connection-check`): its method by the case that applies to it (server-previews when its pages need a server, static-site when they do not, a host's case when the host deploys that app as a project of its own, proven on that app's own address), and its own sign-in when the scan item's `accessByApp` says it has one, each account question's title naming the app (*"Order desk admin account"*). Done when every chosen app's method is saved, or its preview skipped as server-previews says, and its sign-in saved or skipped, and no other app got a write, a build or a question.
11
+ 5. **Set each chosen app up**, one after the other, as the playbook's steps 3 and 4 say for that app alone, `appKey` on every write and check (`preview`, `test-accounts`, `latest-pull-check`, `connection-check`): its method by the case that applies to it (server-previews when its pages need a server, static-site when they do not, a host's case when the host deploys that app as a project of its own, proven on that app's own address: impeccable_hosted_previews, impeccable_netlify_previews and impeccable_cloudflare_previews take its `appKey` and list only the previews of the project or site that builds its folder, so another app's address, project or key never stands in for it; their `unmatched` says why none can be told, and that app goes as server-previews or static-site says, with the reason under "What is not verified"), and its own sign-in when the scan item's `accessByApp` says it has one, each account question's title naming the app (*"Order desk admin account"*). Done when every chosen app's method is saved, or its preview skipped as server-previews says, and its sign-in saved or skipped, and no other app got a write, a build or a question.
13
12
  6. **The pull requests** as pull-requests says, once for the repository: the setup commit takes the state's one workflow `file` (a job for each chosen static site whose preview Impeccable builds) once every chosen app's method and build settings are saved, or its preview skipped as server-previews says; the test drive adds its page to every chosen app with a preview (an app whose preview was skipped gets none, and goes under "What was skipped, and why"), in that app's own framework and linked from that app's own public page, so one review renders each of them on its own preview; that case's step 3 proves each chosen app with the input that proved it; its step 6 checks once, with the `appKey` of the first chosen app with a preview, and the other apps' previews go under "What is not verified". Done when pull-requests' steps are done.
14
13
  - **A chosen app whose preview was skipped** (server-previews): it stays on, with no method, no job in the workflow and no test drive page. It holds nothing back: the other apps' setup commit, test drive and checks go on as above, and its checkup entry left in `next` counts as done for the playbook's step 5. The report says, on its line and under "What was skipped, and why", that it gets no preview until its host deploys them.
15
14
  - **A host previews some chosen apps and Impeccable builds the others**: a host's case speaks for its own app. The setup commit carries the workflow for the others and the test drive branches from it, and pull-requests' step 4 goes each app's way: the host's app proven on the host's build, the others by the workflow. The repository goes without a workflow and a setup pull request only where a host previews every chosen app.
16
- 7. **Report per app**: under "What was configured, and by whom", one line per app the question offered: on or off and why (picked, unticked, the scan's default after a skip), its method, its sign-in, what was verified; an app left out is under "What was skipped, and why" as well, with the setup page as where it is turned on. Done when every app is on one line.
15
+ 7. **Report per app**: under "What was configured, and by whom", one line per app the question offered: on or off and why (picked, unticked, named with `--app`, on already after a skip), its method, its sign-in, what was verified; an app left out is under "What was skipped, and why" as well, with the setup page as where it is turned on. Done when every app is on one line.
17
16
 
18
17
  ### Reference
19
18
 
20
- - **The folders**: the install runs where the manifest the lockfile belongs to is (`build.installDirectory` in the state: the root of a workspace with one lockfile), the build and the start in the app's own folder (`build.workingDirectory`, the `directory` the preview item's `apps` names). impeccable_verify_preview takes no `appKey`: give each step its `directory` accordingly, and `output.directory` from the checkout's root; the saved `outputDir` is relative to the app's folder (`.` when its files are the pages), since the job uploads from there.
19
+ - **The folders**: the install runs where the manifest the lockfile belongs to is (`build.installDirectory` in the state: the root of a workspace with one lockfile), the build and the start in the app's own folder (`build.workingDirectory`, the `directory` the preview item's `apps` names). impeccable_verify_preview takes no `appKey`: give each step its `directory` accordingly, and `output.directory` from the checkout's root; the saved `outputDir` is relative to the app's folder (`.` when its files are the pages), since the job uploads from there. Where the check is skipped on this machine (static-site says when), each app's job proves its build on the pull request.
21
20
  - **Ask nothing else about apps**: names, a split or a merge are the person's on the setup page. A proposal that looks wrong (a shared package, a missing folder) goes into the report, never into a `split`.
22
21
  - **Status lines** name the app (*"Proving the storefront's preview"*), under the playbook's tasks.
23
22
  - The checkup's entries for an app read "<repository> (<app>): …", and its `apps` check is answered by step 1.
23
+ - **Each app's host key** (Vercel's bypass secret, Netlify's site password, Cloudflare's service token) is saved for that app alone, by the tool that gets it in place, and preview-protection's step 2 checks it with that app's `appKey`.
@@ -9,10 +9,12 @@ Netlify deploys previews of the pull requests: impeccable_netlify_previews lists
9
9
 
10
10
  ### Reference
11
11
 
12
+ - **The live app**: `production` in the evidence is the site's own address; live-app-address says how it becomes the before, in step 2's write.
12
13
  - **The aim** holds here: the CLI does the work, and the person is asked only what the playbook's aim allows.
13
14
  - **Behind a password or Netlify's login** (`gate: "netlify-protection"`): the site password, or Netlify's team login, stands in front of the previews. That is the preview-protection case's, whose steps come before step 2: on the person's yes the password is stored, or the site behind the team login made public.
15
+ - **Deploy Previews off now** (`pullRequestPreviews` in the answer, `enabled: false`, with its `reason`): the site built previews before, and its settings build none now. The aim holds: ask once with impeccable_enable_previews (`host: "netlify"`), after step 1's proof and preview-protection's steps and before the test drive is pushed, so the person sees the lock's question and this one back to back, at most one each. `enabled`: go on. `skipped` or `refused`: push no test drive (pull-requests' "No preview, no test drive"), and say under "What was skipped, and why" that Deploy Previews are off for the site, with the `reason`, and name the `page`; `left` stays empty, since the person was asked in this run. `enabled: 'unknown'`: say so in one line under "What is not verified" and go on.
14
16
  - **Another app, or no answer from any of the three**: Netlify's previews do not show this app. Set the preview up as server-previews or static-site says instead, and say under "What is not verified" what the Netlify address answered.
15
17
  - **Nothing listed**: Netlify's previews are not on record. Where the checkout is set up for Netlify that is the netlify-setup case's; else set the preview up as server-previews or static-site says. An `error` means a read failed, so a preview may exist that is not listed: say so under "What is not verified".
16
18
  - **The pull requests**: no setup pull request; the test drive branches from the default branch. Netlify builds a deploy preview only for a pull request, so the test drive opens as a draft, its deploy preview is waited for and the test drive's page proven on it, and then the draft is marked ready: pull-requests' step 4. In place of pull-requests' step 3, build the test drive branch once with the build Netlify runs (`netlify.toml`'s `[build]` `command`, else the package's `build` script), so a page that does not build is never pushed.
17
- - The evidence is read only: link, deploy and change nothing on Netlify, beyond what impeccable_netlify_password does on the person's yes. Netlify does not report its previews as GitHub deployments, so Impeccable finds them by their address pattern: save the pattern the proof answered, and no other.
19
+ - The evidence is read only: link, deploy and change nothing on Netlify, beyond what impeccable_netlify_password and impeccable_enable_previews do on the person's yes. Netlify does not report its previews as GitHub deployments, so Impeccable finds them by their address pattern: save the pattern the proof answered, and no other.
18
20
  - The report names the address that answered with this app under "What we found", and the pattern saved under "What was configured, and by whom". The test drive's own preview, which Netlify builds after the run, goes under "What is not verified".
@@ -4,22 +4,23 @@ The checkout is set up for Netlify: a `netlify.toml`, a `.netlify/` folder, or a
4
4
 
5
5
  ### Steps
6
6
 
7
- 1. **Sign in**, before any other preview work, with impeccable_ask: `kind: "confirm"`, `id: "netlify-setup"`. The tool does the rest: it shows the sign-in screen only when the Netlify CLI is signed out (a person signed in already is asked nothing), runs the login in the browser and offers a retry. Done when it answered `confirmed`.
8
- - **`cli: "missing"`**: install the CLI with the person's package manager (`npm install -g netlify-cli`, `pnpm add -g netlify-cli`, `yarn global add netlify-cli` or `bun add -g netlify-cli`), then ask again: the screen shows no second time.
7
+ 1. **Sign in**, before any other preview work, with impeccable_ask: `kind: "confirm"`, `id: "netlify-setup"`. The tool does the rest: it shows the setup screen once (a person signed in already confirms the setup with that sign-in on it, with no login to run), runs the login in the browser when the Netlify CLI is signed out and offers a retry. Done when it answered `confirmed`.
8
+ - **`cli: "missing"`**: install the CLI with `npm install -g netlify-cli@27.11.2`, exactly so: the run installs it with npm alone, pinned to that version, and refuses another package manager, version or tag; then ask again: the screen shows no second time.
9
9
  - **`teams` in the answer** (the person has several Netlify teams): ask once which owns the site, `kind: "single"`, `id: "netlify-team"`, `options` the teams as given (the tool words the rest), and pass the pick to `sites:create` as `--account-slug`.
10
10
  - **Skip**: set the preview up as server-previews or static-site says, and say under "What was skipped, and why" that Netlify was found and the sign-in was skipped. The steps below do not apply.
11
- 2. **Link or create the site**: `netlify link --git-remote-url <the GitHub origin>` links a site Netlify already builds from this repository; when it finds none, `netlify sites:create --name <name>` creates one in the person's team and links this folder. Name it after the repository: lowercase letters, digits and hyphens, at most 40 characters; a name Netlify says is taken gets `-2`. Done when `netlify status` names the linked project.
12
- 3. **Connect the repository** with impeccable_netlify_connect: it connects the linked site to this repository through Netlify's GitHub app, so pull requests build deploy previews, and asks the person for the app's access when Netlify cannot read the repository. Give `command` and `publish` only where no `netlify.toml` sets the build. Done when it answered `connected: true`; a site `netlify link` found is connected already, and it says so.
11
+ 2. **Link or create the site** (run each Netlify command plain, on its own: a command chained after `sites:create` leaves the site unrecorded): `netlify link --git-remote-url <the GitHub origin>` links a site Netlify already builds from this repository; when it finds none, `netlify sites:create --name <name>` creates one in the person's team and links this folder. Name it after the repository: lowercase letters, digits and hyphens, at most 40 characters; a name Netlify says is taken gets `-2`. Done when `netlify status` names the linked project.
12
+ 3. **Connect the repository** with impeccable_netlify_connect: it connects the linked site to this repository through Netlify's GitHub app, so pull requests build deploy previews, when that site is the one this run created or one built from this repository already (a site built from another repository, or one the run did not create, is left as it is, with the reason), and asks the person for the app's access when Netlify cannot read the repository. Give `command` and `publish` only where no `netlify.toml` sets the build. Done when it answered `connected: true`; a site `netlify link` found is connected already, and it says so. **Deploy Previews off** on that site (`pullRequestPreviews` in its answer, or in impeccable_netlify_previews', `enabled: false`): the aim holds, so ask once with impeccable_enable_previews (`host: "netlify"`) before step 4, and never a second time. `enabled`: step 4. `skipped` or `refused`: the setup ends here, as "A step fails before the test drive is pushed" says, with the `reason` and the `page` in the report, and `left` empty, since the person was asked in this run.
13
13
  4. **Build and push the test drive** (the Test drive task) as pull-requests' steps 2 to 4 say, with the changes netlify-previews' reference gives: from the default branch, no setup pull request, built once with the build Netlify runs. Stage the new page and the page that links to it by name: `netlify sites:create` writes `.netlify/` and may add it to `.gitignore`, and that change stays out of the pull request. Done when `git push -u origin impeccable/test-drive` succeeded.
14
14
  5. **Its draft, its preview proven and saved, then marked ready** as pull-requests' step 4 (Netlify) and step 5 say: the draft, whose deploy preview is the site's first, the wait for it by `pull`, the test drive's page proven on it, impeccable_write to `preview` as netlify-previews' step 2 says, `gh pr ready`, then the recording. The playbook's step 6 then has no pull request left to open: go on with the playbook's step 4. Done when impeccable_test_pr answered "Recorded".
15
15
 
16
16
  ### Reference
17
17
 
18
+ - **The live app**: a site created here has none yet, so the before stays unset, which the report says (live-app-address).
18
19
  - **The aim** holds here: the CLI does the work, and the person is asked only what the playbook's aim allows.
19
20
  - **A step fails before the test drive is pushed** (the sign-in skipped or not completed, the site not created, the repository not connected): the Netlify setup ends there. Set the preview up as server-previews or static-site says, say under "What was skipped, and why" what stopped it, and put the person's step in `left` when there is one, such as "Give Netlify's GitHub app access to the repository", with the `access` page in the report.
20
21
  - **No preview for the draft**: save no method, set up no other preview, and leave the draft as it is, unrecorded, as pull-requests' "No preview, no test drive" says; the site and the connection stay, and the report names them.
21
22
  - **The build failed** (`state` is `error` or `failure`): say so under "What is not verified", with the command that builds this linked checkout as Netlify does and prints why, for the person to run: `netlify deploy --build --context deploy-preview`. It never runs here, since it builds with their Netlify sign-in.
22
- - **No deploy preview within five minutes**: the wait's own screen opens the site's deploy settings, where Deploy Previews are turned on, and waits again on the person's yes. When they skip it, or none came after its retries, say under "What is not verified" that Netlify built no deploy preview here, and that Deploy Previews may be off in the site's settings.
23
+ - **No deploy preview within five minutes**: the wait's own screen opens the site's deploy settings, where Deploy Previews are turned on, and waits again on the person's yes (a site whose settings said they were off was asked about in step 3, so this wait is for a build, not a setting). When they skip it, or none came after its retries, say under "What is not verified" that Netlify built no deploy preview here, and that Deploy Previews may be off in the site's settings.
23
24
  - **Behind a password or Netlify's login** (`gate: "netlify-protection"`): the preview-protection case's, whose steps come before the save in step 5. A team that keeps new sites private puts Netlify's team login in front of the new site's previews: the tool makes the site public on the person's yes, and step 5 goes on once it answers `opened`.
24
- - **The report** names the Netlify site under "What was configured, and by whom", whichever way the run ended, since it stays in the person's Netlify account, with how the repository was connected and the CLI's install when there was one. The site's environment variables are the person's: when the app needs some (`.env.example`), list their names, never their values, under "Before you merge".
25
+ - **The report** names the Netlify site under "What was configured, and by whom", whichever way the run ended, since it stays in the person's Netlify account, with how the repository was connected, Deploy Previews turned on when they were (`by` says whether the run or the person did), and the CLI's install when there was one. The site's environment variables are the person's: when the app needs some (`.env.example`), list their names, never their values, under "Before you merge".
25
26
  - **Status lines**: the sign-in, the link and the connection belong to the Preview method task; from the page on, the Test drive task, so name the test drive in each line about its build, its push, the draft, the wait for its preview, the proof and the save (*"Saving the test drive's preview as the method"*). Word Netlify's login as a login, since a line about signing in belongs to the Sign-in task.
@@ -9,10 +9,11 @@ impeccable_prove_preview answered a `gate`: the host's own lock stands in front
9
9
  - `opened` (Netlify): the site is public now and the preview proven (`proven: true`); `madePublic` says whether the run or the person made it public, and `site` which site. Step 2.
10
10
  - `login` (Netlify): Netlify kept its team login in front through the retries; `reason` says so. Step 3.
11
11
  - Any answer with `madePublic` but `opened`: the run made the Netlify site public and it stays so, though the preview did not open; the report names that change as the bullet below says, whatever step comes next.
12
- - `refused` whose `reason` says to prove a page a visitor opens: Netlify's own site password opened neither the header nor Netlify's form, so the page you proved asks for the app's own sign-in past the lock, or Netlify did not answer. Prove a page a visitor opens without signing in, as netlify-previews' step 1 says, and call the tool again; the consent is not asked a second time. Refused again, step 3.
12
+ - `refused` with `protection: "none"` (Netlify: the site's settings show neither a password nor the team login, or the site is public now, `madePublic` saying who made it so, and the page past the login is not the app's), or on Vercel whose `reason` says the settings show no Deployment Protection: the page you proved is the app's own sign-in or another page, however it is worded, and the previews are open. Prove a page a visitor opens without signing in, as vercel-previews' or netlify-previews' step 1 says, and on `proven: true` go on with that case's step 2, the save, calling this tool no more: there is no lock to get past. No such page proven: the host's previews do not show this app to a visitor, so go on as that case's "Another app, or no answer" says (set the preview up as server-previews or static-site says, and say under "What is not verified" what the address answered), never step 3, whose lock the settings show is not there.
13
+ - Any other `refused` on Netlify whose `reason` says to prove or try a page a visitor opens, or that the page asks for the app's own sign-in past the lock: the site password (Netlify's own, or the one typed) got past the lock to the app's own sign-in on the page you proved, or opened neither the header nor Netlify's form, or Netlify did not answer. Prove a page a visitor opens without signing in, as netlify-previews' step 1 says, and call the tool again; the consent is not asked a second time. Refused again, step 3.
13
14
  - Any other `skipped` or `refused`: nothing was saved; `reason` says what stopped it. Step 3.
14
15
  - `save-failed`: the setup API did not take it all; `reason` says how far it got. Call the tool once more; when it fails again, step 3, with "Save the Vercel bypass secret on the setup page", "Save the Netlify site password on the setup page" or "Save the Cloudflare service token on the setup page" in `left`.
15
- 2. **Check it from Impeccable's side**: impeccable_write to `connection-check` with `{"appKey": "main"}`, and report what it answered. Then go on with the case that sent you here: the proof stands, so save the method as vercel-previews', netlify-previews' or cloudflare-previews' step 2 says, Cloudflare's with the tool's `pattern`. Done when the method is saved.
16
+ 2. **Check it from Impeccable's side**: impeccable_write to `connection-check` with `{"appKey": "main"}` (on a repository with several apps, the app's own key: the tool saved the key for that app), and report what it answered. Then go on with the case that sent you here: the proof stands, so save the method as vercel-previews', netlify-previews' or cloudflare-previews' step 2 says, Cloudflare's with the tool's `pattern`. Done when the method is saved.
16
17
  3. **When nothing was saved**, save no method and set up no other preview, whatever the checkup's `next` says, and open no test drive (pull-requests' "No preview, no test drive"). Say under "What is not verified" that the previews are behind the host's lock, with the tool's `reason`, and name the settings page where the person makes the key, or makes the Netlify site public (the report's bullet below); put the person's one step in `left`, the settings action alone and never running the setup again (for Netlify's team login, none: the person was asked in this run, so `left` stays empty and the report names the Visitor access page); end the report's "Not part of this setup" line with the lock the tool met: "Vercel's Deployment Protection in front of the previews", "Netlify's password in front of the previews", "Netlify's team login in front of the previews", or "Cloudflare Access in front of the previews". Done when the report says so.
17
18
 
18
19
  ### Reference
@@ -6,8 +6,8 @@ Every run, once the settings are saved. The person merges the setup pull request
6
6
 
7
7
  1. **The setup commit**, when the repository needs a change to keep previews working: the workflow and any fix the build needed, nothing else. With the workflow current on the default branch and no fix, there is none. Check access as workflow-as-person says, branch `impeccable/setup` from `origin/<defaultBranch>`, and bring in the workflow and commit as that case says. Push it in step 7, after the test drive. Done when that case's steps are done.
8
8
  2. **The test drive page**, always. The test drive shows the person a whole review: pages to capture, a finding for each measure, and a new page, which is what starts the review's visitor sessions. Branch `impeccable/test-drive` from the setup commit, so it carries the same workflow and its preview runs from its own head (from the default branch when there is no setup commit). Add one small, self-contained page in the repository's own framework, such as `/impeccable-test-drive`: a file where the routing finds pages, plus its styles, with a heading, a paragraph and a few elements in the app's own words (so the review judges it as one of the app's pages), and the flaws below. Link it from a page **a visitor opens without signing in**, the landing page when it is one, and keep the new page open without signing in too: no guard, on the server or in the browser, sends it to the sign-in. Done when the new page carries every flaw below with its comment, and the page you picked links to it.
9
- 3. **Prove both pages** with impeccable_verify_preview, for a preview Impeccable builds itself (static-site): the input that proved the preview, with both pages in `entries`. A host's preview (vercel-previews, vercel-setup, netlify-previews, netlify-setup) is proven on the host's own build in step 4 instead; its case says how the branch is built here first. Done when it answers `verified: true`, or the preview is a host's.
10
- 4. **Push the test drive and prove its preview**: commit the new page **and** the page that links to it, each staged by name, `git push -u origin impeccable/test-drive`, then the way its preview comes:
9
+ 3. **Prove both pages** with impeccable_verify_preview, for a preview Impeccable builds itself (static-site): the input that proved the preview, with both pages in `entries`. A host's preview (vercel-previews, vercel-setup, netlify-previews, netlify-setup) is proven on the host's own build in step 4 instead; its case says how the branch is built here first. Done when it answers `verified: true`, or the preview is a host's, or the check is skipped on this machine (its answer says it has no sandbox for it): then the workflow's job proves both pages on the pull request, under "What is not verified".
10
+ 4. **Push the test drive and prove its preview**: a host whose evidence said `pullRequestPreviews.enabled: false` is asked first, once, as its case says (impeccable_enable_previews; skipped or refused, push nothing: "No preview, no test drive" below); then commit the new page **and** the page that links to it, each staged by name, `git push -u origin impeccable/test-drive`, then the way its preview comes:
11
11
  - **Vercel** (vercel-previews, vercel-setup) builds every push: impeccable_hosted_previews with `branch: "impeccable/test-drive"` waits up to five minutes for its deployment of the pushed head, under a status line with its timer. Prove the test drive's page on that address with impeccable_prove_preview (past Vercel's login as preview-protection says), and save the method as the case says. Then step 5.
12
12
  - **Netlify** (netlify-previews, netlify-setup) builds a deploy preview only for a pull request: open the test drive now as a **draft**, step 5's `gh pr create` with `--draft`, which Impeccable leaves unreviewed. impeccable_netlify_previews with `pull` the number it printed waits up to five minutes for the deploy preview, under a status line with its timer. Prove the test drive's page on it, save the method as the case says, then `gh pr ready impeccable/test-drive --repo <owner>/<repo>` marks it ready, which starts the review. Then step 5's recording.
13
13
  - **Impeccable's own preview** (static-site): the workflow in the branch builds it on the pull request, and Impeccable's review holds up to eight minutes for that job before it renders, so step 3's proof and the workflow in the branch (from the setup commit) are all this step takes. Then step 5.
@@ -26,6 +26,6 @@ Every run, once the settings are saved. The person merges the setup pull request
26
26
  - **Low-contrast text**: text in a solid light grey, well under 4.5:1 against its background.
27
27
  - **A layout transition**: `transition: width 0.3s`, naming the layout property.
28
28
  - **Body text at the viewport edge**: a paragraph of more than 40 characters in the main content, without a background, flush with the sides of the viewport.
29
- - **No preview, no test drive**: when step 4 ends with the preview unproven (the host's build failed or none came within the five minutes, the host's login stayed in front, step 3 failed), open no test drive: mark the Test drive task failed, say under "What is not verified" what was missing and what the person can do, and put their step in `left`. The setup pull request still opens in step 7 when there is a setup commit. A Netlify draft stays open, unrecorded: name it in the report. Put "Mark the test drive #N ready once Netlify builds its preview" in `left` only where Netlify built no preview; where it built one and the lock stayed in front, preview-protection's step 3 says what the report says instead.
29
+ - **No preview, no test drive**: when step 4 ends with the preview unproven (the host's build failed or none came within the five minutes, the host's login stayed in front, step 3 failed), open no test drive: mark the Test drive task failed, say under "What is not verified" what was missing and what the person can do, and put their step in `left` (on Cloudflare, cloudflare-previews' "No pull-request previews" names the cause and the page; a step the person was asked about in this run goes in the report, not in `left`). The setup pull request still opens in step 7 when there is a setup commit. A Netlify draft stays open, unrecorded: name it in the report. Put "Mark the test drive #N ready once Netlify builds its preview" in `left` only where Netlify built no preview; where it built one and the lock stayed in front, preview-protection's step 3 says what the report says instead.
30
30
  - **A single-page app**, whose routes live in the browser: the new page is a self-contained HTML file in the folder the build copies as it is (`public/` in Vite), so the output holds it for `entries` and the review counts it as a page added.
31
31
  - **A branch name an earlier run took** (checkout or push says so): use `-2`, then `-3`, and name the earlier pull request in the report so the person can close it.
@@ -5,8 +5,8 @@ The pages need no server from this repository: plain HTML, or a build that write
5
5
  ### Steps
6
6
 
7
7
  1. **Find the output**: the folder the build writes the pages to (framework config, scripts), or, with no build, the folder that holds the real `index.html`. Done when you can name that folder and the build command, or that there is none.
8
- 2. **Prove it** with impeccable_verify_preview in static mode. Done when it answers `verified: true` with the folder's `index.html` in `files`; a failure names its `stage`, which the report explains, and pull-requests' "No preview, no test drive" says how the run goes on without a proof.
9
- 3. **Save it** with impeccable_write to `preview`: `intendedMethod: "static-upload"`, `outputDir` and the commands you proved, clearing a saved command you did not prove. Done when the answer's `keys` show each key stored or unchanged.
8
+ 2. **Prove it** with impeccable_verify_preview in static mode. Done when it answers `verified: true` with the folder's `index.html` in `files`; a failure names its `stage`, which the report explains, and pull-requests' "No preview, no test drive" says how the run goes on without a proof. When it answers that this machine has no sandbox for the check (Linux, or `--no-sandbox`), the check is skipped: save the build the scripts name in step 3, and the preview job on the pull request proves it; put that under "What is not verified".
9
+ 3. **Save it** with impeccable_write to `preview`: `intendedMethod: "static-upload"`, `outputDir` and the commands you proved, clearing a saved command you did not prove; when the check was skipped on this machine, the build the scripts name is what you save, and the preview job proves it. Done when the answer's `keys` show each key stored or unchanged.
10
10
 
11
11
  ### Reference
12
12
 
@@ -4,13 +4,15 @@ Vercel deploys previews of the pull requests: impeccable_hosted_previews lists a
4
4
 
5
5
  ### Steps
6
6
 
7
- 1. **Prove the newest preview** with impeccable_prove_preview: the address of the newest successful Preview deployment of a pull request (or a page on it a visitor opens without signing in, when the app's own sign-in answers there), and words the page shows that the checkout holds, such as the app's name in its title. Done when it answers `proven: true`; when an address no longer answers, try the next newest, three at most.
7
+ 1. **Prove the newest preview** with impeccable_prove_preview: the address of the newest successful Preview deployment of a pull request (on a repository with several apps, the newest of those impeccable_hosted_previews listed for the app's `appKey`, the deployments of the Vercel project that builds its folder) (or a page on it a visitor opens without signing in, when the app's own sign-in answers there), and words the page shows that the checkout holds, such as the app's name in its title. Done when it answers `proven: true`; when an address no longer answers, try the next newest, three at most.
8
8
  2. **Save the method** with impeccable_write to `preview`: `{"intendedMethod": "preview-deploy"}`. The proof stands in for the person's answer, so ask nothing. Done when the answer's `keys` show `intendedMethod` stored or unchanged.
9
9
 
10
10
  ### Reference
11
11
 
12
+ - **The live app**: with the checkout linked to the project (`.vercel/project.json`) and a production deployment of it on GitHub's record, the evidence answers the project's own domain as `production`; live-app-address says how it becomes the before, in step 2's write. Unlinked, GitHub's record names no address of it that is open.
12
13
  - **The aim** holds here: the CLI does the work, and the person is asked only what the playbook's aim allows.
13
14
  - **Behind Vercel's login** (`gate: "vercel-protection"`): Deployment Protection stands in front of the previews. Get past it as preview-protection says, which proves the preview, and go on with step 2; when nothing got past it, that case says how the run ends.
15
+ - **`pullRequestPreviews` in impeccable_hosted_previews' answer**: what vercel.json and the linked project say of the test drive's branch, read with the evidence and never changed by the run, since the setting is the repository's own or has no command (impeccable_enable_previews takes no Vercel). `enabled: false` (`git.deploymentEnabled` in vercel.json turns the branch off, or Vercel records no deployments on GitHub, where Impeccable finds its previews): push no test drive on Vercel. Set the preview up as server-previews or static-site says, and say in one line under "What was skipped, and why" that Vercel builds no preview of the pull requests, with the `reason`, and the exact change for the person: the vercel.json rule to lift, or Vercel GitHub deployments under the project's Settings → Git. `enabled: 'unknown'` (an Ignored Build Step decides commit by commit, or the CLI could not read the project): go on, and say so in one line under "What is not verified".
14
16
  - **Another app, or no answer from any of the three**: Vercel's previews do not show this app. Set the preview up as server-previews or static-site says instead, and say under "What is not verified" what the Vercel address answered.
15
17
  - **The pull requests**: no setup pull request; the test drive branches from the default branch, and Vercel builds its preview from the push, which pull-requests' step 4 waits for and proves the test drive's page on before the pull request opens. In place of pull-requests' step 3, build the test drive branch once with the install and build Vercel runs (`vercel.json`'s `installCommand` and `buildCommand`, else the package's), as plain commands (`pnpm install`, then `pnpm run build`) and not with impeccable_verify_preview, which is for a preview Impeccable starts itself, so a page that does not build is never pushed.
16
18
  - The evidence is GitHub's: link, deploy and change nothing on Vercel, and save no address pattern, since Vercel reports each preview's address to GitHub itself.
@@ -4,21 +4,25 @@ The checkout is set up for Vercel (`vercel.json`, `.vercel/project.json`, a `ver
4
4
 
5
5
  ### Steps
6
6
 
7
- 1. **Sign in**, before any other preview work, with impeccable_ask: `kind: "confirm"`, `id: "vercel-setup"`. The tool does the rest: it shows the sign-in screen only when the Vercel CLI is signed out (a person signed in already is asked nothing), runs the login in the browser and offers a retry. Done when it answered `confirmed`, with `team` the slug the project goes into.
8
- - **`cli: "missing"`**: install the CLI with the person's package manager (`npm install -g vercel`, `pnpm add -g vercel`, `yarn global add vercel` or `bun add -g vercel`), then ask again: the screen shows no second time.
7
+ 1. **Sign in**, before any other preview work, with impeccable_ask: `kind: "confirm"`, `id: "vercel-setup"`. The tool does the rest: it shows the setup screen once (a person signed in already confirms the setup with that sign-in on it, with no login to run), runs the login in the browser when the Vercel CLI is signed out and offers a retry. Done when it answered `confirmed`, with `team` the slug the project goes into.
8
+ - **`cli: "missing"`**: install the CLI with `npm install -g vercel@62.7.0`, exactly so: the run installs it with npm alone, pinned to that version, and refuses another package manager, version or tag; then ask again: the screen shows no second time.
9
9
  - **`teams` in the answer instead of `team`** (several teams, none current): ask once which owns the project, `kind: "single"`, `id: "vercel-team"`, `options` the teams as given (the tool words the rest). The pick is the team.
10
10
  - **Neither `team` nor `teams`**: the account has no team, and `vercel link --yes` without `--team` links in the person's own scope.
11
11
  - **Skip**: set the preview up as server-previews or static-site says, and say under "What was skipped, and why" that Vercel was found and the sign-in was skipped. The steps below do not apply.
12
- 2. **Create the project** (run each Vercel command plain: its answer is short, and a pipe to `tail` is refused): `vercel link --yes --team <the team>` creates the project in that team, named after the folder (or links the one that has that name), and connects the GitHub `origin`; the CLI names no team on its own without a terminal, so the option is given whenever there is a team. Done when it prints the linked project.
13
- 3. **Connect the repository, and lock the project before its first deployment**, with impeccable_vercel_connect: it runs the connection on the linked project, asks the person for Vercel's GitHub app's access when the app cannot reach the repository, and then, on a project with no production deployment, puts Vercel's login in front of every address of the project, its own included. Vercel makes a new project's first deployment Production whatever branch it builds and serves it at the project's own address, and the test drive's push is that first deployment. When Vercel refuses the lock, the tool asks the person on one screen to turn it on in the project's settings and waits for it. Done when it answered `connected: true` with `production: "exists"` or `locked: true`; a project connected by the link already is, and it says so.
12
+ 2. **Create the project, then link it** (run each Vercel command plain, on its own: its answer is short, a pipe to `tail` is refused, and a command chained after it leaves the project unrecorded): `vercel project add <the folder's name> --team <the team>` creates the project in that team, under the name `vercel link` gives a project (the checkout folder's name, lowercase); then `vercel link --yes --team <the team>`, in the checkout's root (never with `--cwd` or after a `cd`), links the folder to the project of that name, which is now the run's. The CLI names no team on its own without a terminal, so the option is given whenever there is a team. Done when the link prints the linked project.
13
+ - **`vercel project add` fails** (the name is in use: the project is the person's, not this run's): run no link; the Vercel setup ends there, and with the checkout linked to no project its CLI stops and the hold on pushes goes. Go on as "A step fails before the test drive is pushed" says, naming the project under "What was skipped, and why". When `.vercel/project.json` was in the checkout before the run, that link is not this run's, and the hold on pushes stays, by design: no test drive, as "No preview for the pushed test drive" says, and the report says under "What was skipped, and why" that the person removes `.vercel/project.json` or links the project themselves and runs the setup again.
14
+ 3. **Connect the repository, and lock the project before its first deployment**, with impeccable_vercel_connect: it runs the connection on the linked project, when that project is the one step 2 created or one connected to this repository already, asks the person for Vercel's GitHub app's access when the app cannot reach the repository, and then, on a project with no production deployment, puts Vercel's login in front of every address of the project, its own included. Vercel makes a new project's first deployment Production whatever branch it builds and serves it at the project's own address, and the test drive's push is that first deployment. When Vercel refuses the lock, the tool asks the person on one screen to turn it on in the project's settings and waits for it. Done when it answered `connected: true` with `production: "exists"` or `locked: true`; a project connected by the link already is, and it says so.
14
15
  4. **Build and push the test drive** (the Test drive task) as pull-requests' steps 2 to 4 say, with the changes vercel-previews' reference gives: from the default branch, no setup pull request, built once with plain commands. Stage the new page and the page that links to it by name: `vercel link` writes `.vercel/` and a `.env.local` that holds a Vercel token, and may add `.vercel` to `.gitignore`, and none of that goes into the pull request. Its push starts the project's first deployment. Done when `git push -u origin impeccable/test-drive` succeeded.
15
16
  5. **Its preview, proven and saved, then its pull request** as pull-requests' step 4 (Vercel) and step 5 say: the wait for the pushed head's deployment, the test drive's page proven on the address it lists (past Vercel's login as preview-protection says), impeccable_write to `preview` as vercel-previews' step 2 says, then the pull request and its recording. The playbook's step 6 then has no pull request left to open: go on with the playbook's step 4. Done when impeccable_test_pr answered "Recorded".
16
17
 
17
18
  ### Reference
18
19
 
20
+ - **The live app**: a project created here has none yet, which the report says; one that deploys production already (`production: "exists"`) answers its address as `productionUrl`, and live-app-address says how it becomes the before.
19
21
  - **The aim** holds here: the CLI does the work, and the person is asked only what the playbook's aim allows.
20
- - **A step fails before the test drive is pushed** (the sign-in skipped or not completed, the team skipped, the project not created, the repository not connected): the Vercel setup ends there. Set the preview up as server-previews or static-site says, say under "What was skipped, and why" what stopped it, and put the person's step in `left` when there is one, such as "Give Vercel's GitHub app access to the repository", with the `access` page in the report.
22
+ - **A step fails before the test drive is pushed** (the sign-in skipped or not completed, the team skipped, the project not created, the repository not connected, or step 3 answering that the linked project is not this run's: `vercel link` linked one of the person's projects, named like the folder, which is left as it is): the Vercel setup ends there. Set the preview up as server-previews or static-site says, say under "What was skipped, and why" what stopped it, and put the person's step in `left` when there is one, such as "Give Vercel's GitHub app access to the repository", with the `access` page in the report.
23
+ - **`pullRequestPreviews` in impeccable_hosted_previews' answer**: what vercel.json and the linked project say of the test drive's branch, read with the evidence and never changed by the run, since the setting is the repository's own or has no command (impeccable_enable_previews takes no Vercel). `enabled: false` (`git.deploymentEnabled` in vercel.json turns the branch off, or Vercel records no deployments on GitHub, where Impeccable finds its previews): the Vercel setup ends before step 4, as "A step fails before the test drive is pushed" says, and say in one line under "What was skipped, and why" that Vercel builds no preview of the pull requests, with the `reason`, and the exact change for the person: the vercel.json rule to lift, or Vercel GitHub deployments under the project's Settings → Git. `enabled: 'unknown'` (an Ignored Build Step decides commit by commit, or the CLI could not read the project): go on, and say so in one line under "What is not verified".
21
24
  - **A push before step 3** is refused, since a connected project builds every push: run impeccable_vercel_connect first.
25
+ - **On a repository with several apps**, the project step 2 creates builds the repository's root: impeccable_vercel_connect connects and locks it only where an app this run sets up is the root's, and otherwise answers so, and the Vercel setup ends there for that app, as "A step fails before the test drive is pushed" says.
22
26
  - **`locked: false`** from step 3, with `production` not "exists" (the person skipped the tool's screen, or the lock was still off after its retries): push nothing, and set up no other preview either, since the connected project would build that push as its first deployment, public at its own address. Mark the Test drive task failed, and say under "What was skipped, and why" what stopped the lock (the answer's `reason`).
23
27
  - **No preview for the pushed test drive**: save no method, set up no other preview and open no pull request, as pull-requests' "No preview, no test drive" says; the project and the connection stay, and the report names them.
24
28
  - **The build failed** (`state` is `failure` or `error`): `vercel deploy --yes` builds this checkout on Vercel and prints why it failed. When the cause is in the test drive's own change, correct it, commit, push once more and wait for that build; else the report names the cause.
package/dist/playbook.js CHANGED
@@ -15,13 +15,13 @@ export function playbook(repo) {
15
15
  return `You are Impeccable's setup agent, in a checkout of ${repo.owner}/${repo.name} on the person's own machine. This setup covers one situation: Impeccable previews the repository's web front end, taking the previews its host deploys for pull requests (vercel-previews, netlify-previews, cloudflare-previews, or vercel-setup and netlify-setup, which turn them on) or, for a static site, building it itself through the workflow the setup state generates (\`static-upload\`), and signs in with the app's own username-or-email-and-password accounts when it has a sign-in. You read files and run commands here, save Impeccable's settings with the impeccable_* tools, and open pull requests as the person. The person watches this terminal, and a web page mirrors your tasks and the report.
16
16
 
17
17
  # The aim
18
- Set the repository up with whatever the host's CLI can do (Netlify's, Vercel's, Cloudflare's, signed in as the person), through the commands the fence runs and the impeccable_* tools, and find your own way past a roadblock instead of handing it to the person. Ask the fewest questions possible: a sign-in where a CLI is signed out, and a yes before anything in their account changes (*"Make your previews public, so reviews can open them?"*). **A step for the person is the last resort**, only where no command the fence runs and no tool can do it; a refusal is final, as Commands says.
18
+ Set the repository up with whatever the host's CLI can do (Netlify's, Vercel's, Cloudflare's, signed in as the person), through the commands the fence runs and the impeccable_* tools, and find your own way past a roadblock instead of handing it to the person. Ask the fewest questions possible: a sign-in where a CLI is signed out, and a yes before anything in their account changes (*"Make your previews public, so reviews can open them?"*). Set the live app's address as the before yourself where the host or the README names it (live-app-address): the host's own address of the connected project changes nothing of theirs, so it takes no question. **A step for the person is the last resort**, only where no command the fence runs and no tool can do it; a refusal is final, as Commands says.
19
19
 
20
20
  # Steps
21
21
 
22
22
  1. **Read the state.** The setup API's instructions and state came with the first message, trimmed to this setup; impeccable_state reads the state again. Done when you know the preview item's status, \`file\` and \`workflowStanding\`, whether there is a \`signin\` item, whether there is an \`apps\` item (the monorepo case comes first then), and the titles in \`notPartOfThisSetup\`.
23
23
  2. **Read the repository**: manifests, the lockfile, scripts, framework config, CI workflows, env examples, the routes, the auth code and the seed. Done when you can name the package manager, the install, build and start commands, whether the pages need a server, how people sign in, and which pages a visitor opens without signing in.
24
- 3. **Set up the preview** as the vercel-previews case says when impeccable_hosted_previews lists Vercel's previews of the pull requests, as the netlify-previews case says when impeccable_netlify_previews lists Netlify's (and as preview-protection says when the host's login or password stands in front of either), as the cloudflare-previews case says when impeccable_cloudflare_previews lists Cloudflare Pages', as the vercel-setup case says when the checkout is set up for Vercel and Vercel has never deployed it, as the netlify-setup case says when the checkout is set up for Netlify and it lists none, else as the server-previews case says when the pages need a server, and as the static-site case says when they do not. Done when that case's steps are done.
24
+ 3. **Set up the preview** as the vercel-previews case says when impeccable_hosted_previews lists Vercel's previews of the pull requests, as the netlify-previews case says when impeccable_netlify_previews lists Netlify's (and as preview-protection says when the host's login or password stands in front of either), as the cloudflare-previews case says when impeccable_cloudflare_previews lists Cloudflare Pages', as the vercel-setup case says when the checkout is set up for Vercel and Vercel has never deployed it, as the netlify-setup case says when the checkout is set up for Netlify and it lists none, else as the server-previews case says when the pages need a server, and as the static-site case says when they do not; then the live app's address as the live-app-address case says, in the method's write where it can be. Done when those cases' steps are done.
25
25
  4. **Set up the sign-in** as the sign-in case says, when the app has its own. Done when that case's steps are done.
26
26
  5. **Run impeccable_checkup**, do what \`next\` says, and run it again. Done when \`status\` is complete, or all that is left is in \`forPerson\` or \`waitingFor\`.
27
27
  6. **Open the pull requests** as the pull-requests case says. What waits on the person holds neither back. Done when that case's steps are done.
@@ -61,7 +61,7 @@ Keep secrets out of files, settings and messages: the workflow names a repositor
61
61
  ## Voice
62
62
  The person reads your [STATUS] lines, task titles and questions, here and on the web page: calm, specific, one line each.
63
63
  - **[STATUS] lines**: start each step's message with one, a present-tense verb phrase of at most 70 characters saying what is happening (*"Running the build"*, *"Opening the test drive"*), or a short finding (*"Next.js 16, pnpm, Prisma on SQLite. No hosting found."*). impeccable_progress refuses a line that narrates like an assistant: write *"Reading the deploy workflows"*, not *"I'm trying to work out how the app deploys."* Detail belongs in the report.
64
- - **Tasks**: after each task, call impeccable_progress with the whole list. Titles are short noun phrases the person recognizes, the same every run where they fit: Read the repository, Install and build, Preview method, Preview workflow, Sign-in, Test drive, Setup pull request. The list carries only what this run does: add a task when its case starts, never before, and leave off a task no case of this run takes, such as Preview workflow and Setup pull request where a host builds the previews (impeccable_progress refuses them once its previews are the method). Mark a task skipped only when the person skipped it, and failed only when it was tried and failed. One task is in progress at a time, and every status line is the detail of that task (*"Installing with pnpm"* while Install and build is in progress); impeccable_progress refuses a status line about another task.
64
+ - **Tasks**: after each task, call impeccable_progress with the whole list. Titles are short noun phrases the person recognizes, the same every run where they fit: Read the repository, Apps (on a repository with several apps), Install and build, Preview method, Preview workflow, Sign-in, Test drive, Setup pull request. The list carries only what this run does: add a task when its case starts, never before, and leave off a task no case of this run takes, such as Preview workflow and Setup pull request where a host builds the previews (impeccable_progress refuses them once its previews are the method). Mark a task skipped only when the person skipped it, and failed only when it was tried and failed. One task is in progress at a time, and every status line is the detail of that task (*"Installing with pnpm"* while Install and build is in progress); impeccable_progress refuses a status line about another task.
65
65
  - **Questions**: one sentence, at most one hint line, and a title that names the subject.
66
66
 
67
67
  ## The report
package/dist/preflight.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { execFileSync } from 'node:child_process';
2
- import { existsSync, readFileSync, statSync } from 'node:fs';
2
+ import { closeSync, constants, existsSync, fstatSync, openSync, readFileSync, statSync } from 'node:fs';
3
3
  import { join, resolve } from 'node:path';
4
4
  import { parseArgs } from 'node:util';
5
+ import { folderOf } from './host-previews.js';
5
6
  import { UserError } from './log.js';
6
7
  export const EFFORTS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
7
8
  /** The environment variable that names the folder to set up when --install-dir does not. */
@@ -17,6 +18,8 @@ Options:
17
18
  --install-dir <path>
18
19
  the folder to set up, in place of the one the command is
19
20
  run in (IMPECCABLE_REVIEWS_INSTALL_DIR does the same)
21
+ --app <key> on a repository with several apps, set up this one app,
22
+ by its key (such as storefront), without asking which
20
23
  --host <origin> Impeccable's address (default https://impeccable.reviews)
21
24
  --model <id> the model the setup agent runs on (defaults to the model the
22
25
  service hands out)
@@ -31,10 +34,13 @@ Options:
31
34
  where, what it did; never the text) to a JSON file beside
32
35
  the transcript, and print where it is
33
36
  --frames <dir> write each screen into this folder as it changes (for evals)
37
+ --no-sandbox run the setup's commands without a sandbox, with all your
38
+ access, where the command would otherwise stop (Linux without
39
+ bubblewrap, socat and ripgrep; other systems)
34
40
  --no-telemetry report none of the command's steps to Impeccable
35
41
  (DO_NOT_TRACK=1 does the same)
36
42
  -h, --help show this help`;
37
- const LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);
43
+ export const LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);
38
44
  export function parseFlags(argv, env = process.env, cwd = process.cwd()) {
39
45
  let values;
40
46
  try {
@@ -44,6 +50,7 @@ export function parseFlags(argv, env = process.env, cwd = process.cwd()) {
44
50
  allowPositionals: false,
45
51
  options: {
46
52
  'install-dir': { type: 'string' },
53
+ app: { type: 'string' },
47
54
  host: { type: 'string', default: 'https://impeccable.reviews' },
48
55
  debug: { type: 'boolean', default: false },
49
56
  transcript: { type: 'string' },
@@ -52,6 +59,7 @@ export function parseFlags(argv, env = process.env, cwd = process.cwd()) {
52
59
  model: { type: 'string' },
53
60
  effort: { type: 'string', default: 'high' },
54
61
  yes: { type: 'boolean', default: false },
62
+ 'no-sandbox': { type: 'boolean', default: false },
55
63
  'no-telemetry': { type: 'boolean', default: false },
56
64
  help: { type: 'boolean', short: 'h', default: false },
57
65
  },
@@ -82,13 +90,15 @@ export function parseFlags(argv, env = process.env, cwd = process.cwd()) {
82
90
  throw new UserError('--frames needs a folder.');
83
91
  if (values['install-dir'] !== undefined && !values['install-dir'].trim())
84
92
  throw new UserError('--install-dir needs a folder.');
93
+ if (values.app !== undefined && !/^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/.test(values.app.trim()))
94
+ throw new UserError('--app needs an app\'s key, such as storefront.');
85
95
  // The option wins over the environment variable, and a relative path is taken from the folder the command is run in.
86
96
  // The path stays as it was typed: a folder's name may begin or end with a space.
87
97
  const named = values['install-dir'] ?? env[INSTALL_DIR_ENV];
88
98
  const installDir = named?.trim() ? { path: resolve(cwd, named), from: values['install-dir'] !== undefined ? '--install-dir' : INSTALL_DIR_ENV } : undefined;
89
99
  // Paths from the folder the command is run in too: it goes on from the checkout's top.
90
100
  const path = (value) => (value === undefined ? undefined : resolve(cwd, value.trim()));
91
- return { host: host.origin, installDir, debug: values.debug, transcript: path(values.transcript), scanReport: values['scan-report'], frames: path(values.frames), model: values.model?.trim(), effort: values.effort, yes: values.yes, noTelemetry: values['no-telemetry'], help: values.help };
101
+ return { host: host.origin, installDir, ...(values.app !== undefined ? { app: values.app.trim() } : {}), debug: values.debug, transcript: path(values.transcript), scanReport: values['scan-report'], frames: path(values.frames), model: values.model?.trim(), effort: values.effort, yes: values.yes, noSandbox: values['no-sandbox'], noTelemetry: values['no-telemetry'], help: values.help };
92
102
  }
93
103
  const NOT_A_CHECKOUT = { event: 'setup_command_check_failed', outcome: 'not_a_checkout' };
94
104
  /** owner/repo from a GitHub remote address, in any of the forms git accepts. */
@@ -118,6 +128,15 @@ function defaultBranch(cwd, git) {
118
128
  return '';
119
129
  }
120
130
  }
131
+ /**
132
+ * Windows starts a command named without a folder (git, gh, vercel) from the working folder before PATH, so a git.exe
133
+ * committed to the checkout would run as the person before anything is asked. Windows reads this setting from the
134
+ * environment of the process that starts the command, so it goes on this one, before its first command; every command
135
+ * it starts inherits it.
136
+ */
137
+ export function skipWorkingFolderForCommands(env = process.env) {
138
+ env.NoDefaultCurrentDirectoryInExePath = '1';
139
+ }
121
140
  /**
122
141
  * The checkout `cwd` is in. `from` is what named that folder (--install-dir or its environment variable) when it is not
123
142
  * the one the command is run in: the folder is then checked to be there, and what is wrong with it names it.
@@ -161,7 +180,7 @@ export function readCheckout(cwd, from) {
161
180
  const parsed = parseGitHubRemote(remote);
162
181
  if (!parsed)
163
182
  throw new UserError(from ? `The "origin" remote of the checkout in ${root} is not on GitHub. Name the folder of a checkout of a GitHub repository.` : 'The "origin" remote of this checkout is not on GitHub. Run this in a checkout of a GitHub repository.', { event: 'setup_command_check_failed', outcome: 'not_github' });
164
- return { root, ...parsed, protectedBranches: [...new Set([defaultBranch(cwd, git), 'main', 'master'].filter(Boolean))] };
183
+ return { root, start: folderOf(git('rev-parse', '--show-prefix')) ?? '', ...parsed, protectedBranches: [...new Set([defaultBranch(cwd, git), 'main', 'master'].filter(Boolean))] };
165
184
  }
166
185
  /** Dependencies that make a package a web front end, whatever else it depends on. */
167
186
  const WEB_PACKAGES = /^(next|nuxt|vite|astro|svelte|vue|gatsby|react-scripts|solid-js|preact|lit|ember-source|vitepress|htmx\.org|alpinejs|parcel|webpack|react-router-dom|@(angular|remix-run|sveltejs|nuxt|docusaurus|11ty)\/.+|@builder\.io\/qwik)$/;
@@ -172,6 +191,8 @@ const MARKUP = /\.(html?|vue|svelte|astro|php|erb|ejs|hbs|handlebars|mustache|nj
172
191
  const STYLES = /\.(css|scss|sass|less|styl|pcss)$/i;
173
192
  /** Manifests of a project in some language: without one, a checkout with no page in it says nothing. */
174
193
  const MANIFEST = /(^|\/)(package\.json|pubspec\.yaml|go\.mod|Cargo\.toml|pyproject\.toml|requirements\.txt|setup\.py|Pipfile|Gemfile|pom\.xml|build\.gradle(\.kts)?|Package\.swift|mix\.exs|[^/]+\.csproj|project\.pbxproj|AndroidManifest\.xml)$/;
194
+ /** More than any manifest a person writes. */
195
+ const MANIFEST_BYTES = 1024 * 1024;
175
196
  /** The documents every repository has; other Markdown may be a site's own pages (Jekyll, MkDocs, Hugo). */
176
197
  const REPOSITORY_DOCS = /(^|\/)(README|CHANGELOG|CHANGES|HISTORY|LICEN[CS]E|CONTRIBUTING|CODE_OF_CONDUCT|SECURITY|SUPPORT|AUTHORS|NOTICE)[^/]*$|(^|\/)\.github\//i;
177
198
  /** Folders of other people's code, and the shells of the native platforms. */
@@ -186,7 +207,7 @@ const NATIVE_SHELL = /(^|\/)(ios|android|macos|windows|linux)\//;
186
207
  * (react-native-web, Flutter's web folder) may or may not ship it, and Markdown
187
208
  * pages may be a generated site, so those and every other doubt answer "unknown".
188
209
  */
189
- export function checkoutFrontEnd(root) {
210
+ export function checkoutFrontEnd(root, log) {
190
211
  let paths;
191
212
  try {
192
213
  paths = execFileSync('git', ['ls-files', '-z', '--cached', '--others', '--exclude-standard'], { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 256 * 1024 * 1024 })
@@ -196,13 +217,27 @@ export function checkoutFrontEnd(root) {
196
217
  catch {
197
218
  return 'unknown';
198
219
  }
220
+ // A manifest that is a pipe, a device or a huge file would hang or swamp the command before anything is asked: it is skipped.
221
+ // O_NONBLOCK lets a pipe open without a writer, so what it is can be seen first. One not read leaves the answer in doubt.
222
+ let unread = false;
199
223
  const read = (path) => {
224
+ let fd;
200
225
  try {
201
- return readFileSync(join(root, path), 'utf8');
226
+ fd = openSync(join(root, path), constants.O_RDONLY | (constants.O_NONBLOCK ?? 0));
227
+ const stat = fstatSync(fd);
228
+ if (stat.isFile() && stat.size <= MANIFEST_BYTES)
229
+ return readFileSync(fd, 'utf8');
230
+ log?.write('front end: skipped a manifest', { path, file: stat.isFile(), size: stat.size });
202
231
  }
203
232
  catch {
204
- return null;
233
+ // Unreadable: in doubt as well.
234
+ }
235
+ finally {
236
+ if (fd !== undefined)
237
+ closeSync(fd);
205
238
  }
239
+ unread = true;
240
+ return null;
206
241
  };
207
242
  let reactNative = false;
208
243
  let flutter = false;
@@ -243,7 +278,7 @@ export function checkoutFrontEnd(root) {
243
278
  }
244
279
  if (web)
245
280
  return 'web';
246
- if (webTarget || (!native && paths.some((p) => /\.(md|mdx|markdown)$/i.test(p) && !REPOSITORY_DOCS.test(p))))
281
+ if (unread || webTarget || (!native && paths.some((p) => /\.(md|mdx|markdown)$/i.test(p) && !REPOSITORY_DOCS.test(p))))
247
282
  return 'unknown';
248
283
  return native || paths.some((p) => MANIFEST.test(p)) ? 'none' : 'unknown';
249
284
  }
package/dist/report.js CHANGED
@@ -35,6 +35,7 @@ export function createReporter(opts) {
35
35
  method: 'POST',
36
36
  headers: { 'content-type': 'application/json', ...(token ? { authorization: `Bearer ${token}` } : {}) },
37
37
  body: JSON.stringify({ ...about, events }),
38
+ redirect: 'error',
38
39
  signal: AbortSignal.timeout(SEND_MS),
39
40
  }).catch(() => undefined);
40
41
  sending.add(sent);