@impeccable/reviews 0.1.31 → 0.1.33
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/hosting.js +18 -1
- package/dist/main.js +4 -2
- package/dist/tools.js +10 -3
- package/package.json +1 -1
package/dist/hosting.js
CHANGED
|
@@ -33,7 +33,24 @@ export function hostingOf(state, repo) {
|
|
|
33
33
|
return [{ key, liveUrl: address(a.liveUrl), previews }];
|
|
34
34
|
});
|
|
35
35
|
const unread = Array.isArray(hosting.unread) ? hosting.unread.flatMap((u) => text(u) ?? []) : [];
|
|
36
|
-
return { apps, readAt: text(hosting.readAt), unread };
|
|
36
|
+
return { apps, readAt: text(hosting.readAt), unread, ...(hosting.readPending === true ? { readPending: true } : {}) };
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The record once it is whole: while the state says a read of it is pending, the state is read again every few seconds,
|
|
40
|
+
* up to a minute by the clock, each read bounded by what is left of it. Past that, its previews are left out and it
|
|
41
|
+
* counts as never read, so each host tool reads its previews itself, as it does without a record; the live addresses stay.
|
|
42
|
+
*/
|
|
43
|
+
export async function settledHosting(first, repo, readState, opts = {}) {
|
|
44
|
+
const { everyMs = 3_000, upToMs = 60_000, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), now = Date.now } = opts;
|
|
45
|
+
const deadline = now() + upToMs;
|
|
46
|
+
let hosting = hostingOf(first, repo);
|
|
47
|
+
while (hosting?.readPending && now() + everyMs < deadline) {
|
|
48
|
+
await sleep(everyMs);
|
|
49
|
+
const state = await readState(deadline - now());
|
|
50
|
+
if (state.ok)
|
|
51
|
+
hosting = hostingOf(state.data, repo) ?? hosting;
|
|
52
|
+
}
|
|
53
|
+
return hosting?.readPending ? { apps: hosting.apps.map((a) => ({ ...a, previews: [] })), readAt: null, unread: hosting.unread } : hosting;
|
|
37
54
|
}
|
|
38
55
|
/** The reports each host's previews are read from, besides the deployments every read takes. */
|
|
39
56
|
const REPORTS = { netlify: ['statuses', 'comments'], cloudflare: ['check-runs', 'comments'], render: [] };
|
package/dist/main.js
CHANGED
|
@@ -7,7 +7,7 @@ import { runAgent } from './agent.js';
|
|
|
7
7
|
import { runSkillCommand } from './skill.js';
|
|
8
8
|
import { fenceExtension, guardedFileOperations, hostsAgreed, placeOf, previewRetryState } from './fence.js';
|
|
9
9
|
import { accountsSaved, focusInstructions, focusState, severalApps, signInItem } from './focus.js';
|
|
10
|
-
import {
|
|
10
|
+
import { settledHosting } from './hosting.js';
|
|
11
11
|
import { requestJson } from './http.js';
|
|
12
12
|
import { copyText, letTabsHear, openBrowser, progressPage, screenPrompt, setupAuth, signIn } from './login.js';
|
|
13
13
|
import { checkoutJournal } from './leftovers.js';
|
|
@@ -234,6 +234,8 @@ export async function main(argv) {
|
|
|
234
234
|
]);
|
|
235
235
|
if (!instructions.ok || !state.ok)
|
|
236
236
|
throw new UserError(`Impeccable's setup API did not answer: ${instructions.error ?? state.error}.`);
|
|
237
|
+
// A hosting record behind GitHub is read again while the questions below are asked (hosting.ts `settledHosting`).
|
|
238
|
+
const hosting = settledHosting(state.data, token.repository.name, (timeoutMs) => requestJson(`${setupApi}/state`, { token: token.access_token, timeoutMs }, log));
|
|
237
239
|
// When the checkout cannot tell, the setup state's own verdict decides. When it has a web front end Impeccable skipped, the person adds it first.
|
|
238
240
|
if (stateSaysNoFrontEnd(state.data, token.repository.name)) {
|
|
239
241
|
if (frontEnd === 'unknown')
|
|
@@ -395,7 +397,7 @@ export async function main(argv) {
|
|
|
395
397
|
sandbox,
|
|
396
398
|
pushAccess: push.outcome,
|
|
397
399
|
severalApps: severalApps(state.data),
|
|
398
|
-
hosting:
|
|
400
|
+
hosting: await hosting,
|
|
399
401
|
...(flags.app ? { app: flags.app } : {}),
|
|
400
402
|
start: checkout.start,
|
|
401
403
|
...(flags.signedInTestDrive ? { signedInTestDrive: { accountsSaved: accountsSaved(state.data, token.repository.name) } } : {}),
|
package/dist/tools.js
CHANGED
|
@@ -16,7 +16,7 @@ import { checkoutNames, liveShaped, netlifySiteOf, previewHostName, provePreview
|
|
|
16
16
|
import { closedPulls, deploymentsOf, hostingApp, hostingRead, liveHostOf, renderSitesOf } from './hosting.js';
|
|
17
17
|
import { enableRenderPreviews, parseRenderDeployment, readRenderService, RENDER_DASHBOARD, renderSitesNamed } from './render.js';
|
|
18
18
|
import { cloudflareFromHosting, cfRunner, cfStatus, CLOUDFLARE_APP_PAGE, connectCloudflareProject, enableCloudflarePreviews, pagesProject, pagesProjectName, readCloudflarePreviews } from './cloudflare.js';
|
|
19
|
-
import { passCloudflareAccess } from './cloudflare-access.js';
|
|
19
|
+
import { passCloudflareAccess, TOKEN_NAME } from './cloudflare-access.js';
|
|
20
20
|
import { netlifyFromHosting, commandRunner, connectNetlifySite, liveSite, netlifyStatus, netlifyTeams, readNetlifyPreviews, waitForNetlifyAccess } from './netlify.js';
|
|
21
21
|
import { netlifyToken, passNetlifyLock, setSitePassword } from './netlify-password.js';
|
|
22
22
|
import { connectVercelProject, lockedDeploymentHosts, prepareFirstDeployment, projectMadeDeployment, readLinkedProject, readOwnProject, vercelDashboardScope, vercelPreviewsFact, vercelProjectFor, vercelProjectPage, vercelRepoProject, vercelStatus, vercelTeams, waitForVercelAccess, waitForVercelLock } from './vercel.js';
|
|
@@ -213,6 +213,8 @@ function plainReason(text) {
|
|
|
213
213
|
}
|
|
214
214
|
/** Names as a reader lists them: "a", "a and b", "a, b and c". */
|
|
215
215
|
const andList = (names) => (names.length > 1 ? `${names.slice(0, -1).join(', ')} and ${names.at(-1)}` : names.join(''));
|
|
216
|
+
/** The end screen's line for a Cloudflare service token the run created: what it made, on which Access application, and where it is revoked (REN-1187). */
|
|
217
|
+
const accessNote = (application) => `Impeccable created the Cloudflare service token "${TOKEN_NAME}" and its policy on the Access application "${application}". Revoke it in Zero Trust → Access controls → Service credentials; reviews stop until a new one is saved.`;
|
|
216
218
|
/** The end screen's one line for the Netlify sites whose password the run set and saved, set but did not save, or saved as it was: every site in one line (#419). */
|
|
217
219
|
const PASSWORD_NOTES = {
|
|
218
220
|
set: (sites) => sites.length === 1
|
|
@@ -2840,10 +2842,12 @@ export function createTools(ctx) {
|
|
|
2840
2842
|
}
|
|
2841
2843
|
if (testAccounts && response.ok)
|
|
2842
2844
|
retry.signedIn?.saved.add(appsRepo ? String(object(p.body)?.appKey) : '');
|
|
2843
|
-
// The accounts the save signed in with, said on screen: the tasks show no status line (#417).
|
|
2845
|
+
// The accounts the save signed in with, said on screen: the tasks show no status line (#417). The web page moves on with it, not at the model's next status (REN-1183).
|
|
2844
2846
|
const signedIn = testAccounts && response.ok ? signedInLine(response.data, appsRepo ? appName(String(object(p.body)?.appKey)) : undefined) : undefined;
|
|
2845
|
-
if (signedIn)
|
|
2847
|
+
if (signedIn) {
|
|
2846
2848
|
reportDone(signedIn);
|
|
2849
|
+
ctx.sync.update({ status: signedIn });
|
|
2850
|
+
}
|
|
2847
2851
|
// A check that answered, a preview not running yet included, ran for its app; one still turned away did not.
|
|
2848
2852
|
if (check && (response.ok || response.status === 409))
|
|
2849
2853
|
checkedApps.add(typeof object(p.body)?.appKey === 'string' ? String(object(p.body)?.appKey) : '');
|
|
@@ -4202,6 +4206,9 @@ export function createTools(ctx) {
|
|
|
4202
4206
|
});
|
|
4203
4207
|
}
|
|
4204
4208
|
const { value, ...told } = answer;
|
|
4209
|
+
// A token the run made stays in the person's Cloudflare account, saved or not: the end screen says so, and where it is revoked (REN-1187).
|
|
4210
|
+
if (value && told.token === 'created' && told.application)
|
|
4211
|
+
ctx.onKeyCreated?.(accessNote(told.application), `cloudflare-access:${told.application}`);
|
|
4205
4212
|
if (value) {
|
|
4206
4213
|
// What is in hand follows the store, which holds one token: a new one is the one saved now, and the projects the last one opened no longer stand.
|
|
4207
4214
|
if (value !== accessToken)
|