@catalyst-cloud/cli 0.15.1 → 0.15.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +13 -4
- package/dist/args.js +1 -1
- package/dist/capabilities.js +3 -1
- package/dist/cli.js +25 -30
- package/dist/consent-browser.js +3 -1
- package/dist/oauth.js +1 -1
- package/dist/onboard-accounts.js +67 -171
- package/dist/onboard-checklist.js +309 -0
- package/dist/onboard-existing.js +1 -1
- package/dist/onboard-first-ticket.js +199 -0
- package/dist/onboard-github.js +13 -1
- package/dist/onboard-headless.js +6 -4
- package/dist/onboard-housekeeping.js +93 -0
- package/dist/onboard-next.js +124 -13
- package/dist/onboard-ready.js +61 -0
- package/dist/onboard-runtime.js +117 -26
- package/dist/onboard-standalone-copy.js +73 -66
- package/dist/onboard-ui.js +189 -81
- package/dist/onboard-values.js +47 -0
- package/dist/onboard.js +59 -18
- package/dist/ready-copy.js +325 -0
- package/dist/ready.js +35 -98
- package/dist/setup-onboard-copy.js +262 -41
- package/dist/setup-render.js +73 -14
- package/dist/setup.js +6 -3
- package/package.json +2 -2
- package/skills/catalyst-github/SKILL.md +1 -1
- package/skills/catalyst-linear/SKILL.md +1 -1
- package/skills/catalyst-onboard/SKILL.md +3 -3
- package/skills/catalyst-onboard/references/replacing-a-credential.md +9 -15
- package/skills/catalyst-onboard/references/what-a-phase-needs.md +4 -11
- package/skills/catalyst-onboard/scripts/where-am-i.mjs +3 -2
- package/skills/run-this-project/SKILL.md +2 -2
- package/skills/unstick/SKILL.md +1 -1
- package/skills/what-needs-me/SKILL.md +1 -1
- package/skills/what-this-repo-needs/SKILL.md +1 -1
- package/skills/whats-happening/SKILL.md +2 -2
- package/skills/whats-happening/references/coding-accounts.md +1 -1
- package/skills/whats-happening/scripts/snapshot.mjs +1 -1
package/dist/onboard-ui.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { COMPUTER_CHECKS, standalonePlan, standalonePlanNotes, pendingContinuation, } from "./onboard-standalone-copy.js";
|
|
2
2
|
import { trackedPrompt } from "./setup-prompt-frame.js";
|
|
3
3
|
import { createOnboardInterrupts } from "./onboard-interrupts.js";
|
|
4
|
-
import { setupStepView, CONNECTION_REPAIR_REASONS, setupFinalScreen, setupBrowserInstruction,
|
|
4
|
+
import { setupStepView, CONNECTION_REPAIR_REASONS, setupFinalScreen, setupBrowserInstruction, setupCommandWords as say, setupPartHeading, setupPartProgress, setupTrackerRows, SETUP_PART_NUMBERS, SETUP_PART_OF, SETUP_PARTS, } from "./setup-onboard-copy.js";
|
|
5
5
|
import { createOnboardProgress, } from "./onboard-progress.js";
|
|
6
6
|
import { CREATE_TEAM_CHOICE, suggestTeamKey, teamKeyProblem, teamNameProblem, } from "./onboard-team-create.js";
|
|
7
|
+
import { FIRST_TICKET_SAMPLE, FIRST_TICKET_SKIP, } from "./onboard-first-ticket.js";
|
|
7
8
|
import { ONBOARD_STEPS, ONBOARD_TITLES, stepSatisfied, onboardIdentityLines, onboardNextActions, onboardReadyForWork, onboardStepAction, } from "./onboard.js";
|
|
8
9
|
import { onboardStepDetail, onboardReasonText } from "./onboard-next.js";
|
|
9
10
|
export function shouldUseOnboardUi(args, stdoutTty) {
|
|
@@ -12,25 +13,6 @@ export function shouldUseOnboardUi(args, stdoutTty) {
|
|
|
12
13
|
args.flags.yes !== true &&
|
|
13
14
|
args.flags["dry-run"] !== true);
|
|
14
15
|
}
|
|
15
|
-
export const STEP_NUMBERS = {
|
|
16
|
-
signin: 4,
|
|
17
|
-
housekeeping: 5,
|
|
18
|
-
"linear.workspace": 6,
|
|
19
|
-
"linear.personal": 7,
|
|
20
|
-
"linear.team": 8,
|
|
21
|
-
"linear.adopt": 9,
|
|
22
|
-
"linear.automations": 10,
|
|
23
|
-
"github.install": 11,
|
|
24
|
-
"github.personal": 12,
|
|
25
|
-
"github.repos": 13,
|
|
26
|
-
projects: 13,
|
|
27
|
-
accounts: 14,
|
|
28
|
-
capacity: 15,
|
|
29
|
-
runner: 15,
|
|
30
|
-
settings: 16,
|
|
31
|
-
values: 16,
|
|
32
|
-
"first-ticket": 17,
|
|
33
|
-
};
|
|
34
16
|
export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
35
17
|
const abort = new AbortController();
|
|
36
18
|
const options = { ...streams, signal: abort.signal };
|
|
@@ -52,6 +34,15 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
52
34
|
const renderer = deps.renderer;
|
|
53
35
|
const interactive = deps.interactive !== false;
|
|
54
36
|
let group;
|
|
37
|
+
// CTC-4680: the part whose steps are on screen, the journal and plan the tracker reads, and
|
|
38
|
+
// whether boundaries draw the tracker (not on a check-again pass).
|
|
39
|
+
let part;
|
|
40
|
+
// The part of the last step that ended in this run, shown or not: a boundary is a move past it.
|
|
41
|
+
let ranPart;
|
|
42
|
+
let journalSeen;
|
|
43
|
+
let planned = {};
|
|
44
|
+
let rechecking = false;
|
|
45
|
+
let shownPlan;
|
|
55
46
|
let browserOpened = true;
|
|
56
47
|
let questionOpen = false;
|
|
57
48
|
let signinShown = false;
|
|
@@ -81,6 +72,41 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
81
72
|
].includes(id))) &&
|
|
82
73
|
!(id === "signin" && signinShown));
|
|
83
74
|
const title = (id) => setupStepView({ id, state: "running" }).title;
|
|
75
|
+
const number = (id) => SETUP_PART_NUMBERS[id] ?? 0;
|
|
76
|
+
/** A part's heading, then its plan rows, numbered from 1. */
|
|
77
|
+
const planPart = (r, p, journal) => {
|
|
78
|
+
r.heading(setupPartHeading(p, r.traits.unicode));
|
|
79
|
+
for (const row of standalonePlan(journal, planned.scope, planned.runner))
|
|
80
|
+
if (row.part === p)
|
|
81
|
+
r.plan(row.number, row.title, row.detail);
|
|
82
|
+
};
|
|
83
|
+
/** Starting a step in another part: its heading, and when this run has moved past a part, the
|
|
84
|
+
* tracker again with the new part's plan. After the install engine, part 1's checks run unshown,
|
|
85
|
+
* so setup opens on the part 1 boundary. */
|
|
86
|
+
const enterPart = (id) => {
|
|
87
|
+
if (!renderer)
|
|
88
|
+
return;
|
|
89
|
+
const next = SETUP_PART_OF[id];
|
|
90
|
+
if (next === part)
|
|
91
|
+
return;
|
|
92
|
+
const previous = ranPart;
|
|
93
|
+
part = next;
|
|
94
|
+
if (previous === undefined ||
|
|
95
|
+
next <= previous ||
|
|
96
|
+
rechecking ||
|
|
97
|
+
!journalSeen) {
|
|
98
|
+
renderer.heading(setupPartHeading(next, renderer.traits.unicode));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const progress = setupPartProgress(journalSeen, planned.scope);
|
|
102
|
+
const left = progress[previous].total - progress[previous].done;
|
|
103
|
+
renderer.heading(left
|
|
104
|
+
? `Part ${previous} has ${left === 1 ? "1 step" : `${left} steps`} left. Setup carries on with part ${next}.`
|
|
105
|
+
: `Part ${previous} done. ${SETUP_PARTS[previous].done}`);
|
|
106
|
+
renderer.tracker(setupTrackerRows(progress, { next }));
|
|
107
|
+
planPart(renderer, next, journalSeen);
|
|
108
|
+
renderer.blank();
|
|
109
|
+
};
|
|
84
110
|
const stop = () => {
|
|
85
111
|
if (active) {
|
|
86
112
|
if (renderer)
|
|
@@ -103,14 +129,16 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
103
129
|
}
|
|
104
130
|
signinShown = true;
|
|
105
131
|
if (reveal) {
|
|
106
|
-
|
|
107
|
-
renderer.
|
|
108
|
-
renderer.begin(4, title("signin"), "signing in…");
|
|
132
|
+
enterPart("signin");
|
|
133
|
+
renderer.begin(number("signin"), title("signin"), "signing in…");
|
|
109
134
|
active = true;
|
|
110
135
|
}
|
|
111
136
|
}
|
|
112
137
|
if (hidden(currentStep))
|
|
113
138
|
return;
|
|
139
|
+
// The sign-in's own wait line: the frame already shows the countdown and how to stop.
|
|
140
|
+
if (renderer && /^Waiting for you to approve…/.test(text))
|
|
141
|
+
return;
|
|
114
142
|
if (renderer &&
|
|
115
143
|
currentStep === "linear.adopt" &&
|
|
116
144
|
text.endsWith("already has every state and label"))
|
|
@@ -124,7 +152,7 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
124
152
|
}
|
|
125
153
|
if (currentStep === "capacity")
|
|
126
154
|
return;
|
|
127
|
-
renderer.detail(text);
|
|
155
|
+
renderer.detail(say(text));
|
|
128
156
|
return;
|
|
129
157
|
}
|
|
130
158
|
stop();
|
|
@@ -136,9 +164,9 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
136
164
|
if (hidden(currentStep))
|
|
137
165
|
return;
|
|
138
166
|
if (renderer && currentStep)
|
|
139
|
-
renderer.begin(
|
|
167
|
+
renderer.begin(number(currentStep), title(currentStep), text === ONBOARD_TITLES[currentStep] ? "checking…" : text);
|
|
140
168
|
else if (renderer)
|
|
141
|
-
renderer.line(text);
|
|
169
|
+
renderer.line(say(text));
|
|
142
170
|
else
|
|
143
171
|
spin.start(text);
|
|
144
172
|
active = true;
|
|
@@ -147,7 +175,7 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
147
175
|
const question = () => {
|
|
148
176
|
if (renderer && currentStep && !questionOpen) {
|
|
149
177
|
stop();
|
|
150
|
-
renderer.begin(
|
|
178
|
+
renderer.begin(number(currentStep), title(currentStep), "", "ask");
|
|
151
179
|
active = true;
|
|
152
180
|
questionOpen = true;
|
|
153
181
|
}
|
|
@@ -155,7 +183,7 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
155
183
|
const select = async (opts, ownFrame = false) => {
|
|
156
184
|
if (!renderer)
|
|
157
185
|
return prompts.select(opts);
|
|
158
|
-
const detached = ownFrame || !currentStep;
|
|
186
|
+
const detached = ownFrame || (!currentStep && !questionOpen);
|
|
159
187
|
if (detached) {
|
|
160
188
|
stop();
|
|
161
189
|
renderer.begin(0, "Your choice", "", "ask");
|
|
@@ -202,14 +230,15 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
202
230
|
renderer.heading(screen.heading);
|
|
203
231
|
if (screen.paused)
|
|
204
232
|
renderer.line("Your progress is saved.");
|
|
233
|
+
renderer.tracker(setupTrackerRows(setupPartProgress(journal, planned.scope), "end"));
|
|
205
234
|
if (screen.readinessCause)
|
|
206
|
-
renderer.line(screen.readinessCause);
|
|
235
|
+
renderer.line(say(screen.readinessCause));
|
|
207
236
|
if (screen.actions.length) {
|
|
208
237
|
renderer.line(`${screen.actions.length} ${screen.actions.length === 1 ? "thing needs" : "things need"} you:`);
|
|
209
|
-
screen.actions.forEach((action, i) => renderer.
|
|
238
|
+
screen.actions.forEach((action, i) => renderer.item(`${i + 1}. `, say(action.text)));
|
|
210
239
|
if (screen.more)
|
|
211
240
|
renderer.line(`and ${screen.more} more after these`);
|
|
212
|
-
const continuation = pendingContinuation(journal);
|
|
241
|
+
const continuation = pendingContinuation(journal, new Set(screen.actions.map((action) => action.id)));
|
|
213
242
|
if (continuation)
|
|
214
243
|
renderer.line(continuation);
|
|
215
244
|
}
|
|
@@ -227,6 +256,8 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
227
256
|
interactive,
|
|
228
257
|
handlesSignals: true,
|
|
229
258
|
plan(journal, identity, planOptions) {
|
|
259
|
+
planned = { scope: planOptions?.scope, runner: planOptions?.runner };
|
|
260
|
+
journalSeen = journal;
|
|
230
261
|
if (deps.consentGiven)
|
|
231
262
|
return;
|
|
232
263
|
machineScope = (planOptions?.scope ?? ONBOARD_STEPS).filter((id) => COMPUTER_CHECKS.some((check) => check === id));
|
|
@@ -235,32 +266,46 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
235
266
|
stop();
|
|
236
267
|
if (!introduced) {
|
|
237
268
|
if (renderer)
|
|
238
|
-
renderer.
|
|
269
|
+
renderer.brand("setup", deps.version);
|
|
239
270
|
else
|
|
240
271
|
prompts.intro("Catalyst setup", { output: streams.output });
|
|
241
272
|
introduced = true;
|
|
242
273
|
}
|
|
243
274
|
if (identity !== undefined)
|
|
244
275
|
for (const line of onboardIdentityLines(identity))
|
|
245
|
-
|
|
276
|
+
if (renderer)
|
|
277
|
+
renderer.line(line);
|
|
278
|
+
else
|
|
279
|
+
message(line);
|
|
280
|
+
// After sign-in the plan is asked about again. When it is the plan already on screen, the
|
|
281
|
+
// person sees who they are now and the question, not the header, tracker and plan twice.
|
|
282
|
+
const planKey = JSON.stringify([
|
|
283
|
+
planned.scope,
|
|
284
|
+
planned.runner,
|
|
285
|
+
planOptions?.localSync ?? journal.localSync === true,
|
|
286
|
+
]);
|
|
287
|
+
if (renderer && planKey === shownPlan)
|
|
288
|
+
return;
|
|
289
|
+
shownPlan = planKey;
|
|
246
290
|
if (renderer) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
291
|
+
// CTC-4680: what setup does and the three parts first, then only the first part's steps.
|
|
292
|
+
// Each later part lists its own steps when it starts.
|
|
293
|
+
const first = standalonePlan(journal, planned.scope, planned.runner)[0]?.part ?? 1;
|
|
294
|
+
renderer.blank();
|
|
295
|
+
renderer.line("Setup gets Catalyst working on your team's Linear tickets. It has three parts.");
|
|
296
|
+
renderer.tracker(setupTrackerRows(setupPartProgress(journal, planned.scope), {
|
|
297
|
+
plan: first,
|
|
298
|
+
}));
|
|
299
|
+
planPart(renderer, first, journal);
|
|
300
|
+
// Its steps follow under this heading; the first one does not print it again.
|
|
301
|
+
part = first;
|
|
257
302
|
renderer.line(standalonePlanNotes(planOptions?.localSync ?? journal.localSync === true));
|
|
258
303
|
return;
|
|
259
304
|
}
|
|
260
305
|
const steps = new Map(journal.steps.map((step) => [step.id, step]));
|
|
261
306
|
message(ONBOARD_STEPS.map((id) => `· ${ONBOARD_TITLES[id]}${steps.get(id)?.state === "done" ? " (recheck)" : ""}`).join("\n"));
|
|
262
307
|
message("Use cloud reads by default. Local sync is optional for SQL or sustained local reads.");
|
|
263
|
-
message("Setup
|
|
308
|
+
message("Setup reads your AI accounts without sending them a request.");
|
|
264
309
|
},
|
|
265
310
|
async confirmPlan(localSync, signin = "unavailable") {
|
|
266
311
|
if (deps.consentGiven || !interactive)
|
|
@@ -482,6 +527,36 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
482
527
|
}
|
|
483
528
|
return typeof answer === "string" ? answer : null;
|
|
484
529
|
},
|
|
530
|
+
async chooseFirstTicket(tickets, { teamKey, stage }) {
|
|
531
|
+
stop();
|
|
532
|
+
if (abort.signal.aborted)
|
|
533
|
+
return null;
|
|
534
|
+
const answer = await select({
|
|
535
|
+
...options,
|
|
536
|
+
message: `Which ticket should Catalyst start first? It moves to ${stage} in ${teamKey}.`,
|
|
537
|
+
initialValue: tickets[0]?.identifier ?? FIRST_TICKET_SAMPLE,
|
|
538
|
+
options: [
|
|
539
|
+
...tickets.map((ticket) => ({
|
|
540
|
+
value: ticket.identifier,
|
|
541
|
+
label: `${ticket.identifier} ${ticket.title}`,
|
|
542
|
+
...(ticket.estimate !== null
|
|
543
|
+
? { hint: `estimate ${ticket.estimate}` }
|
|
544
|
+
: {}),
|
|
545
|
+
})),
|
|
546
|
+
{
|
|
547
|
+
value: FIRST_TICKET_SAMPLE,
|
|
548
|
+
label: "Create a sample ticket",
|
|
549
|
+
hint: "documents how to run the tests; changes only CONTRIBUTING.md",
|
|
550
|
+
},
|
|
551
|
+
{ value: FIRST_TICKET_SKIP, label: "Skip for now" },
|
|
552
|
+
],
|
|
553
|
+
});
|
|
554
|
+
if (prompts.isCancel(answer)) {
|
|
555
|
+
abort.abort();
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
return typeof answer === "string" ? answer : null;
|
|
559
|
+
},
|
|
485
560
|
async chooseRunner() {
|
|
486
561
|
stop();
|
|
487
562
|
if (abort.signal.aborted)
|
|
@@ -508,13 +583,44 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
508
583
|
}
|
|
509
584
|
return answer === "yes";
|
|
510
585
|
},
|
|
586
|
+
async retryTimedOut(id) {
|
|
587
|
+
if (abort.signal.aborted)
|
|
588
|
+
return false;
|
|
589
|
+
if (renderer) {
|
|
590
|
+
// The question draws under this row, so the reason stays on screen while it is asked.
|
|
591
|
+
stop();
|
|
592
|
+
renderer.begin(number(id), title(id), "the link timed out", "ask");
|
|
593
|
+
active = true;
|
|
594
|
+
questionOpen = true;
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
stop();
|
|
598
|
+
prompts.log.message(`${title(id)}: the link timed out.`, {
|
|
599
|
+
output: streams.output,
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
const answer = await select({
|
|
603
|
+
...options,
|
|
604
|
+
message: "Ready to try again?",
|
|
605
|
+
initialValue: "again",
|
|
606
|
+
options: [
|
|
607
|
+
{ value: "again", label: "Yes, give me a new link" },
|
|
608
|
+
{ value: "stop", label: "Stop here. Run catalyst setup later to carry on." },
|
|
609
|
+
],
|
|
610
|
+
});
|
|
611
|
+
if (prompts.isCancel(answer) || answer !== "again") {
|
|
612
|
+
abort.abort();
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
return true;
|
|
616
|
+
},
|
|
511
617
|
stagedSigninEnd(state, cause) {
|
|
512
618
|
if (!renderer)
|
|
513
619
|
return false;
|
|
514
620
|
if (state === "done")
|
|
515
621
|
stagedSigninApproved = true;
|
|
516
622
|
if (currentStep === "signin" && signinShown)
|
|
517
|
-
renderer.step(state === "done" ? "done" : state === "failed" ? "fail" : "act",
|
|
623
|
+
renderer.step(state === "done" ? "done" : state === "failed" ? "fail" : "act", number("signin"), title("signin"), state === "done"
|
|
518
624
|
? "approved"
|
|
519
625
|
: state === "failed"
|
|
520
626
|
? "could not verify this account"
|
|
@@ -526,11 +632,11 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
526
632
|
signinShown = false;
|
|
527
633
|
if (state !== "done") {
|
|
528
634
|
if (cause)
|
|
529
|
-
renderer.line(cause.charAt(0).toUpperCase() + cause.slice(1));
|
|
635
|
+
renderer.line(say(cause.charAt(0).toUpperCase() + cause.slice(1)));
|
|
530
636
|
renderer.heading(abort.signal.aborted ? "Setup paused" : "Setup is not ready yet");
|
|
531
637
|
if (!cause?.includes("Your saved connection was not changed."))
|
|
532
638
|
renderer.line("Your saved connection was not changed.");
|
|
533
|
-
renderer.outro("Next: run catalyst
|
|
639
|
+
renderer.outro("Next: run catalyst setup to sign in again.");
|
|
534
640
|
}
|
|
535
641
|
return true;
|
|
536
642
|
},
|
|
@@ -548,29 +654,17 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
548
654
|
if (!machineScope.length)
|
|
549
655
|
return;
|
|
550
656
|
if (machineChecks.size === 0 && !machineBlocked) {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
renderer.begin(1, "This computer", "checking…");
|
|
657
|
+
enterPart(id);
|
|
658
|
+
renderer.begin(number(id), "This computer", "checking…");
|
|
554
659
|
active = true;
|
|
555
660
|
}
|
|
556
661
|
return;
|
|
557
662
|
}
|
|
558
663
|
if (hidden(id))
|
|
559
664
|
return;
|
|
560
|
-
if (id === "projects"
|
|
665
|
+
if (id === "projects")
|
|
561
666
|
return;
|
|
562
|
-
|
|
563
|
-
["signin", "daemon", "housekeeping", "ready"].includes(id)
|
|
564
|
-
? "This computer"
|
|
565
|
-
: id.startsWith("linear.")
|
|
566
|
-
? "Linear"
|
|
567
|
-
: id.startsWith("github.")
|
|
568
|
-
? "GitHub"
|
|
569
|
-
: "Work";
|
|
570
|
-
if (next !== group) {
|
|
571
|
-
renderer.heading(next);
|
|
572
|
-
group = next;
|
|
573
|
-
}
|
|
667
|
+
enterPart(id);
|
|
574
668
|
start(ONBOARD_TITLES[id]);
|
|
575
669
|
return;
|
|
576
670
|
}
|
|
@@ -592,6 +686,9 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
592
686
|
start(ONBOARD_TITLES[id]);
|
|
593
687
|
},
|
|
594
688
|
stepEnd(step, journal) {
|
|
689
|
+
if (journal)
|
|
690
|
+
journalSeen = journal;
|
|
691
|
+
ranPart = SETUP_PART_OF[step.id];
|
|
595
692
|
if (renderer) {
|
|
596
693
|
if (standaloneComputer(step.id)) {
|
|
597
694
|
machineChecks.set(step.id, step);
|
|
@@ -622,20 +719,18 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
622
719
|
if ((step.id === "github.repos" || step.id === "settings") &&
|
|
623
720
|
step.state === "done")
|
|
624
721
|
return;
|
|
625
|
-
if (
|
|
626
|
-
journal?.steps.find((s) => s.id ===
|
|
722
|
+
if (step.id === "projects" &&
|
|
723
|
+
journal?.steps.find((s) => s.id === "github.repos")?.state !== "done")
|
|
627
724
|
return;
|
|
628
725
|
const view = setupStepView(step, journal);
|
|
629
|
-
if (hidden(step.id))
|
|
630
|
-
|
|
631
|
-
group = "This computer";
|
|
632
|
-
}
|
|
726
|
+
if (hidden(step.id))
|
|
727
|
+
enterPart(step.id);
|
|
633
728
|
renderer.step(view.mark, view.number, view.title, view.outcome);
|
|
634
729
|
const granted = deps.verbose ? onboardStepDetail(step) : undefined;
|
|
635
730
|
if (granted)
|
|
636
731
|
renderer.detail(granted);
|
|
637
732
|
if (deps.verbose && CONNECTION_REPAIR_REASONS.has(step.reason ?? ""))
|
|
638
|
-
renderer.detail(onboardReasonText(step, { baseUrl: deps.baseUrl?.(), journal }));
|
|
733
|
+
renderer.detail(say(onboardReasonText(step, { baseUrl: deps.baseUrl?.(), journal })));
|
|
639
734
|
if (view.mark === "fail")
|
|
640
735
|
renderer.detail(deps.logPath?.()
|
|
641
736
|
? "Check the full log for the cause, then try this step again."
|
|
@@ -663,6 +758,16 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
663
758
|
prompts.log.message(granted, { output: streams.output });
|
|
664
759
|
},
|
|
665
760
|
message,
|
|
761
|
+
note(text) {
|
|
762
|
+
if (hidden(currentStep))
|
|
763
|
+
return;
|
|
764
|
+
if (renderer)
|
|
765
|
+
renderer.detail(text);
|
|
766
|
+
else {
|
|
767
|
+
stop();
|
|
768
|
+
prompts.log.message(text, { output: streams.output });
|
|
769
|
+
}
|
|
770
|
+
},
|
|
666
771
|
async checkAgain(journal) {
|
|
667
772
|
if (!renderer ||
|
|
668
773
|
!interactive ||
|
|
@@ -691,7 +796,8 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
691
796
|
machineScope = COMPUTER_CHECKS.filter((id) => !stepSatisfied(journal.steps.find((step) => step.id === id)));
|
|
692
797
|
machineChecks.clear();
|
|
693
798
|
machineBlocked = false;
|
|
694
|
-
|
|
799
|
+
part = undefined;
|
|
800
|
+
rechecking = true;
|
|
695
801
|
renderer.heading("Checking again");
|
|
696
802
|
return true;
|
|
697
803
|
},
|
|
@@ -707,8 +813,8 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
707
813
|
renderer.line(`${ONBOARD_TITLES[only]} finished. Onboarding still has other steps.`);
|
|
708
814
|
else
|
|
709
815
|
for (const action of onboardNextActions(journal, deps.baseUrl?.(), only))
|
|
710
|
-
renderer.line(action);
|
|
711
|
-
renderer.outro("Next: run catalyst
|
|
816
|
+
renderer.line(say(action));
|
|
817
|
+
renderer.outro("Next: run catalyst setup");
|
|
712
818
|
return;
|
|
713
819
|
}
|
|
714
820
|
if (renderer) {
|
|
@@ -721,7 +827,7 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
721
827
|
renderer.line("Your progress is saved.");
|
|
722
828
|
}
|
|
723
829
|
summaryShown = false;
|
|
724
|
-
renderer.outro(screen.next);
|
|
830
|
+
renderer.outro(say(screen.next));
|
|
725
831
|
return;
|
|
726
832
|
}
|
|
727
833
|
stop();
|
|
@@ -745,10 +851,12 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
745
851
|
text = `${ONBOARD_TITLES[only]} finished. Onboarding still has other steps.\nresume: catalyst onboard`;
|
|
746
852
|
prompts.outro(text, { output: streams.output });
|
|
747
853
|
},
|
|
748
|
-
async wait(text, run) {
|
|
749
|
-
const browser = currentStep &&
|
|
750
|
-
|
|
751
|
-
|
|
854
|
+
async wait(text, run, page) {
|
|
855
|
+
const browser = currentStep && renderer && page
|
|
856
|
+
? page
|
|
857
|
+
: currentStep &&
|
|
858
|
+
renderer &&
|
|
859
|
+
setupBrowserInstruction(currentStep, deps.baseUrl?.(), currentStep !== "accounts" && browserOpened);
|
|
752
860
|
const signinWait = Boolean(renderer && currentStep === "signin" && signinShown);
|
|
753
861
|
const seconds = signinWait
|
|
754
862
|
? Math.ceil((deps.signinTimeoutMs ?? 600000) / 1000)
|
|
@@ -760,11 +868,9 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
760
868
|
};
|
|
761
869
|
if (renderer && browser && currentStep) {
|
|
762
870
|
stop();
|
|
763
|
-
renderer.begin(
|
|
871
|
+
renderer.begin(number(currentStep), title(currentStep), renderer.traits.unicode
|
|
764
872
|
? status()
|
|
765
873
|
: "waiting for you, up to 10 minutes");
|
|
766
|
-
if (browser.preparation)
|
|
767
|
-
renderer.detail(browser.preparation);
|
|
768
874
|
renderer.action(browser.instruction);
|
|
769
875
|
if (browser.url)
|
|
770
876
|
renderer.detail(renderer.link(browser.url));
|
|
@@ -847,6 +953,8 @@ export function createClackOnboardUi(prompts, streams, deps = {}) {
|
|
|
847
953
|
delete ui.chooseFirstRepository;
|
|
848
954
|
delete ui.reviewSettings;
|
|
849
955
|
delete ui.chooseRunner;
|
|
956
|
+
delete ui.chooseFirstTicket;
|
|
957
|
+
delete ui.retryTimedOut;
|
|
850
958
|
delete ui.confirmWorkflowAdoption;
|
|
851
959
|
}
|
|
852
960
|
return ui;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { loadConfig } from "./config.js";
|
|
2
|
+
import { loadContract } from "./contract.js";
|
|
3
|
+
import { selectedOnboardTeam } from "./onboard-existing.js";
|
|
4
|
+
import { valuesFacts } from "./ready-copy.js";
|
|
5
|
+
const waiting = (reason, evidence) => ({
|
|
6
|
+
state: "waiting",
|
|
7
|
+
reason,
|
|
8
|
+
...(evidence ? { evidence } : {}),
|
|
9
|
+
});
|
|
10
|
+
/** The selected team's `required_values` readiness check decides this step. It passes when the
|
|
11
|
+
* selected repositories declare no required value, or every one has a value. Nothing here reads,
|
|
12
|
+
* imports or prints a value: a missing one is named, with the page where it is set. A stale, absent
|
|
13
|
+
* or unknown check waits, so the step never claims values it could not see. */
|
|
14
|
+
export function onboardValuesAdapter() {
|
|
15
|
+
return {
|
|
16
|
+
check: async (ctx, journal, signal) => {
|
|
17
|
+
const cfg = loadConfig(ctx.home);
|
|
18
|
+
const teamId = selectedOnboardTeam(journal);
|
|
19
|
+
if (!cfg || !teamId)
|
|
20
|
+
return waiting("required_values_unverified");
|
|
21
|
+
let loaded;
|
|
22
|
+
try {
|
|
23
|
+
loaded = await loadContract(ctx, cfg, { refresh: true, signal });
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return waiting(signal?.aborted ? "interrupted" : "required_values_unverified");
|
|
27
|
+
}
|
|
28
|
+
if (loaded.source === "cache")
|
|
29
|
+
return waiting("required_values_unverified");
|
|
30
|
+
const readiness = loaded.doc.teams.find((team) => team.id === teamId)?.readiness;
|
|
31
|
+
const fresh = readiness?.checkedAt != null &&
|
|
32
|
+
readiness.expiresAt != null &&
|
|
33
|
+
readiness.expiresAt > ctx.now().getTime();
|
|
34
|
+
const check = fresh
|
|
35
|
+
? readiness.checks.find((row) => row.id === "required_values")
|
|
36
|
+
: undefined;
|
|
37
|
+
if (check?.state === "pass")
|
|
38
|
+
return { state: "done", evidence: { team: teamId } };
|
|
39
|
+
if (check?.state !== "fail")
|
|
40
|
+
return waiting("required_values_unverified");
|
|
41
|
+
return waiting("required_values_missing", {
|
|
42
|
+
team: teamId,
|
|
43
|
+
requiredValues: JSON.stringify(valuesFacts(check)),
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|