@impeccable/reviews 0.1.1 → 0.1.2
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/README.md +25 -7
- package/dist/agent.js +2 -2
- package/dist/bin.js +38 -0
- package/dist/cloudflare-access.js +157 -49
- package/dist/cloudflare.js +93 -51
- package/dist/fence.js +106 -38
- package/dist/host-questions.js +4 -3
- package/dist/hosted.js +29 -8
- package/dist/log.js +6 -1
- package/dist/login.js +8 -6
- package/dist/main.js +76 -23
- package/dist/netlify-password.js +2 -1
- package/dist/netlify.js +4 -1
- package/dist/playbook/cases/cloudflare-previews.md +2 -2
- package/dist/playbook/cases/netlify-setup.md +1 -1
- package/dist/playbook/cases/preview-protection.md +3 -3
- package/dist/playbook/github-previews.js +101 -0
- package/dist/playbook/netlify-pattern.js +24 -0
- package/dist/playbook/netlify-previews.js +144 -0
- package/dist/playbook/probe-preview.js +67 -0
- package/dist/playbook/protection-secrets.js +15 -0
- package/dist/playbook/public-preview-request.js +96 -0
- package/dist/playbook/publication-wait.js +46 -0
- package/dist/playbook/vercel-protection.js +121 -0
- package/dist/playbook.js +1 -0
- package/dist/preflight.js +52 -12
- package/dist/report.js +68 -0
- package/dist/sandbox.js +280 -0
- package/dist/tools.js +54 -34
- package/dist/ui/look.js +13 -5
- package/dist/ui/run-state.js +9 -9
- package/dist/ui/screens.js +15 -16
- package/package.json +2 -1
package/dist/ui/look.js
CHANGED
|
@@ -230,9 +230,15 @@ function runPage(scene, f, inner, st) {
|
|
|
230
230
|
: t.status === 'skipped' ? '–'
|
|
231
231
|
: '○';
|
|
232
232
|
const ink = t.status === 'in_progress' || t.status === 'failed' ? st.full : t.status === 'skipped' ? st.faint : st.soft;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
233
|
+
const row = (detail) => `${mark} ${t.title}${detail ? ` · ${detail}` : ''}`;
|
|
234
|
+
if (t.status === 'skipped' || t.status === 'failed')
|
|
235
|
+
return ink(row(t.status));
|
|
236
|
+
if (i !== running || !scene.wait)
|
|
237
|
+
return ink(row());
|
|
238
|
+
// The task in progress says what the run waits for and the time so far, never how long it may wait, so a screen
|
|
239
|
+
// waiting on a host's build is never still; the time alone where the words would push it off the row.
|
|
240
|
+
const waiting = row(`waiting for ${scene.wait.host ? `${scene.wait.host}'s preview` : 'the preview'} · ${scene.wait.timer}`);
|
|
241
|
+
return ink(visibleWidth(waiting) <= inner ? waiting : row(scene.wait.timer));
|
|
236
242
|
});
|
|
237
243
|
const counted = scene.tasks.filter((t) => t.status !== 'skipped');
|
|
238
244
|
const skipped = scene.tasks.length - counted.length;
|
|
@@ -277,8 +283,10 @@ function pageOf(f, inner) {
|
|
|
277
283
|
switch (scene.kind) {
|
|
278
284
|
case 'wait':
|
|
279
285
|
return { heading: START.heading, text: said(START.text, inner, st), rule: divider(inner, st), action: [center(st.faint('One moment…'), inner)] };
|
|
280
|
-
case 'intro':
|
|
281
|
-
|
|
286
|
+
case 'intro': {
|
|
287
|
+
const note = scene.note ? ['', ...block(hang(scene.note, Math.min(TEXT_WIDTH, inner), '! ').map(st.soft), inner)] : [];
|
|
288
|
+
return { heading: START.heading, text: said(START.text, inner, st), rule: divider(inner, st), action: [center(st.full("Let's start with login"), inner), ...note], ways: [['Continue', 'Enter']] };
|
|
289
|
+
}
|
|
282
290
|
case 'signin': {
|
|
283
291
|
// P still takes a pasted code on a remote shell; --help says so, the screen does not.
|
|
284
292
|
const under = scene.note ?? (scene.copied ? 'Copied to clipboard!' : '');
|
package/dist/ui/run-state.js
CHANGED
|
@@ -24,7 +24,7 @@ export class RunState {
|
|
|
24
24
|
tasks = [];
|
|
25
25
|
previous;
|
|
26
26
|
current;
|
|
27
|
-
/** A bounded wait under way: the current line carries its timer, counting up
|
|
27
|
+
/** A bounded wait under way: the current line carries its timer, counting up. */
|
|
28
28
|
waiting;
|
|
29
29
|
/** When something last changed on screen: a status, the task list, an answered question. */
|
|
30
30
|
changedAt;
|
|
@@ -51,12 +51,12 @@ export class RunState {
|
|
|
51
51
|
this.touch();
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
|
-
* The wait
|
|
55
|
-
* `live` false gives the wait's start, "0:00
|
|
56
|
-
* a wait begins or ends, so the frames record the wait once rather than once a second.
|
|
54
|
+
* The wait under way, which the task in progress carries on the tasks screen: the host whose preview it is and the
|
|
55
|
+
* time so far, "1:05". `live` false gives the wait's start, "0:00", the same on every tick: a drawing that changes
|
|
56
|
+
* only when a wait begins or ends, so the frames record the wait once rather than once a second.
|
|
57
57
|
*/
|
|
58
58
|
timer(live = true) {
|
|
59
|
-
return this.waiting
|
|
59
|
+
return this.waiting && { host: this.waiting.host, timer: timer(live ? this.now() - this.waiting.since : 0) };
|
|
60
60
|
}
|
|
61
61
|
setTasks(tasks) {
|
|
62
62
|
this.tasks = tasks.map((t) => ({ ...t }));
|
|
@@ -70,14 +70,14 @@ export class RunState {
|
|
|
70
70
|
* The band's two lines as they read now: after a minute with nothing new the
|
|
71
71
|
* last line moves up, dimmed, under "Still working…" and the time since.
|
|
72
72
|
* `quiet` is false while the person is being asked. During a bounded wait
|
|
73
|
-
* the current line carries the timer, "1:05
|
|
73
|
+
* the current line carries the timer, "1:05", so the screen never
|
|
74
74
|
* looks stalled; `live` false holds the timer at the wait's start, for a
|
|
75
75
|
* drawing that changes only when the words do.
|
|
76
76
|
*/
|
|
77
77
|
band(quiet = true, live = true) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
const wait = this.timer(live);
|
|
79
|
+
if (wait && this.current)
|
|
80
|
+
return { previous: this.previous, current: `${this.current} · ${wait.timer}` };
|
|
81
81
|
const silence = this.now() - this.changedAt;
|
|
82
82
|
if (quiet && silence >= SILENCE_MS)
|
|
83
83
|
return { previous: this.current, current: `Still working… ${elapsed(silence)}` };
|
package/dist/ui/screens.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { format } from 'node:util';
|
|
3
3
|
import { CURSOR_MARKER, getCapabilities, Input, KeybindingsManager, matchesKey, ProcessTerminal, setKeybindings, TUI_KEYBINDINGS, TuiAltScreen } from '@earendil-works/pi-tui';
|
|
4
|
-
import { readCheckout } from '../preflight.js';
|
|
5
4
|
import { copyText } from '../login.js';
|
|
6
5
|
import { workStarted } from '../stages.js';
|
|
7
6
|
import { mountUi } from './bridge.js';
|
|
8
7
|
import { createFrameWriter, frameState } from './frames.js';
|
|
9
8
|
import { drawFrame, OUTCOME, PRODUCT, palette } from './look.js';
|
|
10
9
|
import { RunState } from './run-state.js';
|
|
10
|
+
/** What the command does before a screen ends it at once (Ctrl+C before a run, or a signal): report the stop (main.ts), briefly. */
|
|
11
|
+
let beforeExit = async () => { };
|
|
12
|
+
export function onScreenExit(fn) {
|
|
13
|
+
beforeExit = fn;
|
|
14
|
+
}
|
|
11
15
|
/** How often what moves on screen moves: the loader, the spinner. */
|
|
12
16
|
const TICK_MS = 80;
|
|
13
17
|
/** The run screen's first line, before the agent has said anything. */
|
|
@@ -44,7 +48,7 @@ class Wizard {
|
|
|
44
48
|
onExit = () => this.stop();
|
|
45
49
|
onSignal = (signal) => {
|
|
46
50
|
this.stop();
|
|
47
|
-
process.kill(process.pid, signal);
|
|
51
|
+
void beforeExit('terminated').finally(() => process.kill(process.pid, signal));
|
|
48
52
|
};
|
|
49
53
|
constructor() {
|
|
50
54
|
// The alternate screen's own scrolling and search keys stay off: the wizard draws every row, and a typed answer keeps Home and End.
|
|
@@ -228,7 +232,8 @@ class Wizard {
|
|
|
228
232
|
const run = this.views.find((v) => v.kind === 'run');
|
|
229
233
|
if (!run) {
|
|
230
234
|
this.stop();
|
|
231
|
-
process.exit(130);
|
|
235
|
+
void beforeExit('ctrl_c').finally(() => process.exit(130));
|
|
236
|
+
return;
|
|
232
237
|
}
|
|
233
238
|
this.stopAsked = 0;
|
|
234
239
|
for (const question of this.questions.splice(0))
|
|
@@ -330,7 +335,7 @@ class Wizard {
|
|
|
330
335
|
if (!view)
|
|
331
336
|
return { kind: 'wait' };
|
|
332
337
|
if (view.kind === 'intro')
|
|
333
|
-
return { kind: 'intro' };
|
|
338
|
+
return { kind: 'intro', note: view.note };
|
|
334
339
|
if (view.kind === 'signin')
|
|
335
340
|
return { kind: 'signin', link: view.link, copied: view.copied, again: view.again, note: view.pasting ? undefined : view.note };
|
|
336
341
|
if (view.kind === 'pull')
|
|
@@ -401,30 +406,24 @@ function host() {
|
|
|
401
406
|
wizard ??= new Wizard();
|
|
402
407
|
return wizard;
|
|
403
408
|
}
|
|
404
|
-
function repository() {
|
|
405
|
-
try {
|
|
406
|
-
const checkout = readCheckout(process.cwd());
|
|
407
|
-
return `${checkout.owner}/${checkout.repo}`;
|
|
408
|
-
}
|
|
409
|
-
catch {
|
|
410
|
-
return undefined;
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
409
|
/** Writes every screen into `dir` as it changes (--frames), for the eval. */
|
|
414
410
|
export function recordFrames(dir) {
|
|
415
411
|
frames = createFrameWriter(dir);
|
|
416
412
|
}
|
|
417
|
-
/** The first screen: what the command does, and the way on. Resolves true to go on, false to cancel. */
|
|
418
|
-
export function showIntro() {
|
|
413
|
+
/** The first screen: what the command does, a warning to read before anything runs (`note`), and the way on; without a terminal, a line naming `repository`, the owner/name being set up. Resolves true to go on, false to cancel. */
|
|
414
|
+
export function showIntro(repository, note) {
|
|
419
415
|
const screen = host();
|
|
420
416
|
if (!screen) {
|
|
421
|
-
console.log(`${PRODUCT}: setting up ${repository
|
|
417
|
+
console.log(`${PRODUCT}: setting up ${repository}.`);
|
|
418
|
+
if (note)
|
|
419
|
+
console.log(`! ${note}`);
|
|
422
420
|
console.log();
|
|
423
421
|
return Promise.resolve(true);
|
|
424
422
|
}
|
|
425
423
|
return new Promise((resolve) => {
|
|
426
424
|
const view = {
|
|
427
425
|
kind: 'intro',
|
|
426
|
+
note,
|
|
428
427
|
done: (ok) => {
|
|
429
428
|
screen.remove(view);
|
|
430
429
|
if (ok)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@impeccable/reviews",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Set up a repository for Impeccable for PRs from your terminal.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"private": false,
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"eval": "npm run build && node dist/eval/main.js"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"@anthropic-ai/sandbox-runtime": "0.0.78",
|
|
27
28
|
"@earendil-works/pi-coding-agent": "~0.87.1",
|
|
28
29
|
"@earendil-works/pi-tui": "~0.87.1"
|
|
29
30
|
},
|