premanmcp 0.15.0 → 0.15.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 +8 -3
- package/bin/connect/guide.js +64 -45
- package/bin/connect.js +2 -2
- package/bin/desktop.js +61 -4
- package/bin/integrations.js +48 -38
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -114,9 +114,14 @@ push testing alone, and `--no-guide` connects and nothing else. With `--guide`,
|
|
|
114
114
|
takes every step's default without asking and `--no-runner` / `--no-desktop` /
|
|
115
115
|
`--no-integrations` skip one each.
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
The desktop app is installed rather than offered. It was a question for a while, defaulted
|
|
118
|
+
to no on the grounds that nobody should get a 150MB download by pressing Enter, and what
|
|
119
|
+
that produced was a setup whose last act was to describe the app and decline to install
|
|
120
|
+
it. The build is Developer ID signed and notarized, it is checked against the sha512
|
|
121
|
+
published with the release before anything is copied into `/Applications`, and nothing
|
|
122
|
+
arrives through a browser so there is no quarantine flag and no Gatekeeper warning on
|
|
123
|
+
first launch. `--no-desktop`, or `PREMAN_NO_DESKTOP=1` where no flag can be passed, skips
|
|
124
|
+
it; `install-desktop` does it on its own.
|
|
120
125
|
|
|
121
126
|
In CI or any non-interactive shell, run `connect --agent <name> --api-key pm_live_…`.
|
|
122
127
|
Without `--agent` there is nothing to prompt on, so `connect` prints ready-to-paste
|
package/bin/connect/guide.js
CHANGED
|
@@ -74,11 +74,11 @@ export function whatIsLeftBlock(args) {
|
|
|
74
74
|
/**
|
|
75
75
|
* Ask, or take the step's own default when nobody can answer.
|
|
76
76
|
*
|
|
77
|
-
* `--yes` returns
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
77
|
+
* `--yes` returns each step's own default rather than a blanket yes, so a step
|
|
78
|
+
* that should not ride in on a keypress can say so. The desktop app used to be
|
|
79
|
+
* the only one that did, on the grounds that `--yes` should not mean 150MB into
|
|
80
|
+
* /Applications unattended; it does not ask at all now, and `PREMAN_NO_DESKTOP`
|
|
81
|
+
* or `--no-desktop` is how a run that does not want it says so.
|
|
82
82
|
*/
|
|
83
83
|
export async function confirm(question, { assumeYes = false, defaultYes = true } = {}) {
|
|
84
84
|
if (assumeYes) return defaultYes;
|
|
@@ -149,7 +149,13 @@ export async function discoverEndpoints(
|
|
|
149
149
|
args,
|
|
150
150
|
agent,
|
|
151
151
|
serverName,
|
|
152
|
-
{
|
|
152
|
+
{
|
|
153
|
+
blockedHere = "",
|
|
154
|
+
force = false,
|
|
155
|
+
basePath = ".",
|
|
156
|
+
frameworkHint = undefined,
|
|
157
|
+
handOffBrief = true,
|
|
158
|
+
} = {}
|
|
153
159
|
) {
|
|
154
160
|
try {
|
|
155
161
|
const before = await endpointCounts(args);
|
|
@@ -183,6 +189,20 @@ export async function discoverEndpoints(
|
|
|
183
189
|
// Say why before printing homework: "here is a brief" reads as PreMan not
|
|
184
190
|
// working, when the actual answer is one login away.
|
|
185
191
|
if (blocker) process.stdout.write(`${MARK.skip()} ${blocker}\n`);
|
|
192
|
+
// The brief is written for an agent to execute, not for a person to read.
|
|
193
|
+
// Twenty lines of it in the middle of a setup walk reads as PreMan having
|
|
194
|
+
// failed, and the walk still has questions to ask after this. `endpoints
|
|
195
|
+
// discover` prints the same brief whenever it is actually wanted, so the
|
|
196
|
+
// pointer loses nothing but the wall of text.
|
|
197
|
+
if (!handOffBrief) {
|
|
198
|
+
if (!blocker) {
|
|
199
|
+
process.stdout.write(`${MARK.skip()} no coding agent here to read this repo\n`);
|
|
200
|
+
}
|
|
201
|
+
process.stdout.write(
|
|
202
|
+
` Map it from inside one: ${cliInvocation()} endpoints discover\n`
|
|
203
|
+
);
|
|
204
|
+
return before;
|
|
205
|
+
}
|
|
186
206
|
for (const line of brief.instructions || []) process.stdout.write(`${line}\n`);
|
|
187
207
|
const who = agent ? agent.label : "your coding agent";
|
|
188
208
|
process.stdout.write(`\nHand this brief to ${who}, then run:\n${manualSteps()}`);
|
|
@@ -367,38 +387,38 @@ async function setUpRunner(args, agent, { assumeYes }) {
|
|
|
367
387
|
}
|
|
368
388
|
}
|
|
369
389
|
|
|
370
|
-
/**
|
|
371
|
-
* The question, word for word, wherever the desktop app is offered.
|
|
372
|
-
*
|
|
373
|
-
* Shared because both walks ask it and they must not drift: "install the
|
|
374
|
-
* desktop app?" got yes from people who thought they were agreeing to a menu
|
|
375
|
-
* bar icon, then spent two minutes watching an unexplained download. The size
|
|
376
|
-
* and the destination are the two facts that change the answer, and the last
|
|
377
|
-
* clause is there because the honest answer to "do I need this?" is no.
|
|
378
|
-
*/
|
|
379
|
-
export function desktopQuestion(args) {
|
|
380
|
-
const dest = args.value("--dest", "/Applications");
|
|
381
|
-
return `Download the PreMan desktop app? ~150MB, installs to ${dest} — optional, everything works without it`;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
390
|
/** Where this run would put the app, honouring the same --dest the install does. */
|
|
385
391
|
function desktopAppPath(args) {
|
|
386
392
|
return path.join(args.value("--dest", "/Applications"), "PreMan.app");
|
|
387
393
|
}
|
|
388
394
|
|
|
389
395
|
/**
|
|
390
|
-
*
|
|
396
|
+
* An opt-out for runs that cannot pass a flag.
|
|
391
397
|
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
398
|
+
* A bare `preman` with no arguments is a real entry point and now installs the
|
|
399
|
+
* app, which is right for a customer and wrong for a CI image or a suite that
|
|
400
|
+
* drives the walk end to end. `--no-desktop` cannot reach either of those, so
|
|
401
|
+
* they get the environment variable instead. Same spelling as PREMAN_NO_BROWSER
|
|
402
|
+
* and PREMAN_NO_AGENT, including that 0/false/no mean the opt-out is off.
|
|
403
|
+
*/
|
|
404
|
+
function noDesktopEnv() {
|
|
405
|
+
const optOut = (process.env.PREMAN_NO_DESKTOP || "").trim().toLowerCase();
|
|
406
|
+
return Boolean(optOut) && !["0", "false", "no"].includes(optOut);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Why the desktop app should not be installed here, or null to go ahead.
|
|
411
|
+
*
|
|
412
|
+
* Split out from the step so a caller running its own loop can decide whether
|
|
413
|
+
* the step is worth listing *before* listing it. Both of these were bugs when
|
|
414
|
+
* the check lived after: a Linux user got a macOS-only step, and someone who
|
|
415
|
+
* already had the app watched it download again.
|
|
396
416
|
*/
|
|
397
417
|
export function desktopPrecheck(args) {
|
|
398
|
-
if (args.has("--no-desktop")) return { state: "skipped" };
|
|
418
|
+
if (args.has("--no-desktop") || noDesktopEnv()) return { state: "skipped" };
|
|
399
419
|
// An explicit --desktop is honoured whatever the platform: someone who asked
|
|
400
420
|
// for the app by name is owed the macOS-only notice and the download link
|
|
401
|
-
// rather than silence. Only the unasked
|
|
421
|
+
// rather than silence. Only the unasked install is withheld where there is no
|
|
402
422
|
// build, because a step that announces itself to say "not for you" reads as
|
|
403
423
|
// something being broken.
|
|
404
424
|
if (process.platform !== "darwin" && !args.has("--desktop")) return { state: "unsupported" };
|
|
@@ -411,7 +431,7 @@ export function desktopPrecheck(args) {
|
|
|
411
431
|
*
|
|
412
432
|
* `install` is injectable because onboarding does more than this one does — it
|
|
413
433
|
* hands the fresh session to the app so it opens signed in — and the part worth
|
|
414
|
-
* sharing is the guards
|
|
434
|
+
* sharing is the guards and the wording, not the install itself.
|
|
415
435
|
*/
|
|
416
436
|
export async function runDesktopInstall(args, { install = null } = {}) {
|
|
417
437
|
try {
|
|
@@ -422,14 +442,22 @@ export async function runDesktopInstall(args, { install = null } = {}) {
|
|
|
422
442
|
}
|
|
423
443
|
}
|
|
424
444
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
445
|
+
/**
|
|
446
|
+
* Install the desktop app. Not a question.
|
|
447
|
+
*
|
|
448
|
+
* It used to be one, defaulted to no, on the grounds that the download is large
|
|
449
|
+
* and nobody should get it by pressing Enter. What that produced was a setup
|
|
450
|
+
* whose last act was to describe a thing and decline to do it — the customer
|
|
451
|
+
* still had to go install the app, only now by hand and later. The download is
|
|
452
|
+
* the whole reason a terminal is better placed to do this than a download page:
|
|
453
|
+
* the build is Developer ID signed and notarized, it lands in /Applications
|
|
454
|
+
* verified against the checksum published with the release, and because nothing
|
|
455
|
+
* here came through a browser there is no quarantine flag and so no Gatekeeper
|
|
456
|
+
* warning on first launch. Opting out is `--no-desktop`.
|
|
457
|
+
*/
|
|
458
|
+
async function offerDesktop(args) {
|
|
431
459
|
const already = desktopPrecheck(args);
|
|
432
|
-
// Silent on Linux and Windows: there is no build to
|
|
460
|
+
// Silent on Linux and Windows: there is no build to install, and a step that
|
|
433
461
|
// announces itself only to say "not for you" reads as something being wrong.
|
|
434
462
|
if (already && already.state !== "installed") return already;
|
|
435
463
|
step("Desktop app");
|
|
@@ -437,15 +465,6 @@ async function offerDesktop(args, { assumeYes } = {}) {
|
|
|
437
465
|
process.stdout.write(`${MARK.ok()} PreMan desktop app already installed.\n`);
|
|
438
466
|
return already;
|
|
439
467
|
}
|
|
440
|
-
// Default no, unlike every other step here: this one downloads a hundred-odd
|
|
441
|
-
// megabytes and writes to /Applications, which nobody should get by pressing
|
|
442
|
-
// Enter to move past a prompt. `--desktop` is how an unattended run opts in,
|
|
443
|
-
// since `--yes` deliberately no longer does.
|
|
444
|
-
const wanted = args.has("--desktop");
|
|
445
|
-
if (!wanted && !(await confirm(desktopQuestion(args), { assumeYes, defaultYes: false }))) {
|
|
446
|
-
desktopSkipped();
|
|
447
|
-
return { state: "skipped" };
|
|
448
|
-
}
|
|
449
468
|
return runDesktopInstall(args);
|
|
450
469
|
}
|
|
451
470
|
|
|
@@ -585,7 +604,7 @@ export async function guidedFirstRun(args, agent, apiKey, serverName, { blockedH
|
|
|
585
604
|
step("Runner");
|
|
586
605
|
await setUpRunner(args, agent, { assumeYes });
|
|
587
606
|
|
|
588
|
-
await offerDesktop(args
|
|
607
|
+
await offerDesktop(args);
|
|
589
608
|
|
|
590
609
|
step("Integrations");
|
|
591
610
|
await connectIntegrations(args, apiKey, { assumeYes });
|
package/bin/connect.js
CHANGED
|
@@ -144,11 +144,11 @@ Connect options:
|
|
|
144
144
|
and the integration prompts after connecting
|
|
145
145
|
--no-guide Connect only: no push hook, no closing summary
|
|
146
146
|
--no-runner With --guide, do not pair this machine as a runner
|
|
147
|
-
--no-desktop With --guide,
|
|
147
|
+
--no-desktop With --guide, skip the desktop app, which otherwise
|
|
148
|
+
installs. PREMAN_NO_DESKTOP=1 does the same
|
|
148
149
|
--no-integrations With --guide, do not offer GitHub / AWS / Slack
|
|
149
150
|
--no-hook Do not install the git pre-push hook
|
|
150
151
|
--yes Take every step's default without prompting
|
|
151
|
-
(the desktop app defaults to no; install-desktop)
|
|
152
152
|
--print Print the config instead of writing it
|
|
153
153
|
`;
|
|
154
154
|
|
package/bin/desktop.js
CHANGED
|
@@ -243,13 +243,62 @@ async function expectedDigest(arch) {
|
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
|
|
246
|
+
const BYTES_PER_MB = 1024 * 1024;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* A single line that rewrites itself, or nothing at all when not on a terminal.
|
|
250
|
+
*
|
|
251
|
+
* Onboarding no longer asks before installing the app, and a step nobody agreed
|
|
252
|
+
* to cannot also go quiet for ninety seconds: a still cursor during a 150 MB
|
|
253
|
+
* fetch is indistinguishable from a hang, and the reflex is Ctrl-C halfway
|
|
254
|
+
* through. Redirected output gets the summary line that already existed rather
|
|
255
|
+
* than a few hundred carriage returns, which is why this is TTY-only.
|
|
256
|
+
*/
|
|
257
|
+
function progressLine() {
|
|
258
|
+
if (!process.stdout.isTTY) return { tick: () => {}, clear: () => {} };
|
|
259
|
+
let lastDrawn = 0;
|
|
260
|
+
let width = 0;
|
|
261
|
+
const tick = (received, total) => {
|
|
262
|
+
const now = Date.now();
|
|
263
|
+
// One redraw per chunk is thousands of writes for an effect the eye cannot
|
|
264
|
+
// follow. The final chunk always draws, so the line never stops short.
|
|
265
|
+
if (now - lastDrawn < 150 && received !== total) return;
|
|
266
|
+
lastDrawn = now;
|
|
267
|
+
const got = (received / BYTES_PER_MB).toFixed(1);
|
|
268
|
+
const text = total
|
|
269
|
+
? ` ${got} of ${(total / BYTES_PER_MB).toFixed(1)} MB ${Math.floor((received / total) * 100)}%`
|
|
270
|
+
: ` ${got} MB`;
|
|
271
|
+
// Padded to the widest line drawn so far, or a shorter one leaves the tail
|
|
272
|
+
// of its predecessor on screen.
|
|
273
|
+
width = Math.max(width, text.length);
|
|
274
|
+
process.stdout.write(`\r${text.padEnd(width)}`);
|
|
275
|
+
};
|
|
276
|
+
const clear = () => {
|
|
277
|
+
if (width) process.stdout.write(`\r${" ".repeat(width)}\r`);
|
|
278
|
+
};
|
|
279
|
+
return { tick, clear };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function download(url, destination, onProgress) {
|
|
247
283
|
const controller = new AbortController();
|
|
248
284
|
const timer = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS);
|
|
249
285
|
try {
|
|
250
286
|
const resp = await fetch(url, { signal: controller.signal, redirect: "follow" });
|
|
251
287
|
if (!resp.ok) throw new Error(`download failed: ${resp.status} ${url}`);
|
|
252
|
-
|
|
288
|
+
// Read the body in chunks rather than as one arrayBuffer: the buffering is
|
|
289
|
+
// the same either way, but only this version can say how far along it is.
|
|
290
|
+
const total = Number(resp.headers.get("content-length")) || 0;
|
|
291
|
+
const reader = resp.body.getReader();
|
|
292
|
+
const chunks = [];
|
|
293
|
+
let received = 0;
|
|
294
|
+
for (;;) {
|
|
295
|
+
const { done, value } = await reader.read();
|
|
296
|
+
if (done) break;
|
|
297
|
+
chunks.push(value);
|
|
298
|
+
received += value.length;
|
|
299
|
+
onProgress?.(received, total);
|
|
300
|
+
}
|
|
301
|
+
const buffer = Buffer.concat(chunks);
|
|
253
302
|
writeFileSync(destination, buffer);
|
|
254
303
|
return buffer.length;
|
|
255
304
|
} finally {
|
|
@@ -335,8 +384,16 @@ export async function installDesktopCommand(commandArgs = [], { onInstalled } =
|
|
|
335
384
|
let mounted = null;
|
|
336
385
|
|
|
337
386
|
try {
|
|
338
|
-
const
|
|
339
|
-
|
|
387
|
+
const progress = progressLine();
|
|
388
|
+
let bytes;
|
|
389
|
+
try {
|
|
390
|
+
bytes = await download(url, dmgPath, progress.tick);
|
|
391
|
+
} finally {
|
|
392
|
+
// Cleared even when the download throws, or the error prints onto the
|
|
393
|
+
// half-drawn progress line.
|
|
394
|
+
progress.clear();
|
|
395
|
+
}
|
|
396
|
+
process.stdout.write(` ${(bytes / BYTES_PER_MB).toFixed(1)} MB\n`);
|
|
340
397
|
|
|
341
398
|
const expected = await expectedDigest(arch);
|
|
342
399
|
if (expected) {
|
package/bin/integrations.js
CHANGED
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
|
|
17
17
|
import { describeCheckout, explainNoCheckout, resolveCheckout } from "./repo.js";
|
|
18
18
|
import {
|
|
19
|
+
DEFAULT_BACKEND,
|
|
19
20
|
assertOk,
|
|
21
|
+
backendUrl,
|
|
20
22
|
callBackendJson,
|
|
21
23
|
cliInvocation,
|
|
22
24
|
frontendUrl,
|
|
@@ -86,10 +88,14 @@ function showOpeningDesktop() {
|
|
|
86
88
|
};
|
|
87
89
|
}
|
|
88
90
|
|
|
91
|
+
const INSTALLATIONS_URL = "https://github.com/settings/installations";
|
|
92
|
+
|
|
89
93
|
function present(url, what) {
|
|
90
94
|
process.stdout.write(`\n ${url}\n\n`);
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
// A tab appearing is its own confirmation; saying so as well spends a line
|
|
96
|
+
// on what the customer is already looking at. It earns its place only when
|
|
97
|
+
// no tab opened and the link is the way through.
|
|
98
|
+
if (!openUrl(url)) process.stdout.write(`Open the link above to ${what}.\n`);
|
|
93
99
|
}
|
|
94
100
|
|
|
95
101
|
/**
|
|
@@ -293,9 +299,7 @@ export async function githubCommand(args) {
|
|
|
293
299
|
{
|
|
294
300
|
timeoutMs: Number(process.env.PREMAN_GITHUB_POLL_MS) || GITHUB_POLL_TIMEOUT_MS,
|
|
295
301
|
hintAfterMs: 20000,
|
|
296
|
-
hint:
|
|
297
|
-
"Still nothing from GitHub. Picking at least one repository and confirming\n" +
|
|
298
|
-
"is what sends you back here.",
|
|
302
|
+
hint: "Still nothing from GitHub — confirm a repository to come back here.",
|
|
299
303
|
}
|
|
300
304
|
);
|
|
301
305
|
|
|
@@ -319,15 +323,12 @@ function githubHandOff(args, refresh) {
|
|
|
319
323
|
if (installed) {
|
|
320
324
|
return (
|
|
321
325
|
`The App is installed, but no repositories are shared with it.\n` +
|
|
322
|
-
`
|
|
323
|
-
` - Then re-run '${cliInvocation()} github'.\n`
|
|
326
|
+
` Add some at ${INSTALLATIONS_URL}, then re-run '${cliInvocation()} github'.\n`
|
|
324
327
|
);
|
|
325
328
|
}
|
|
326
329
|
return (
|
|
327
|
-
`Nothing from GitHub yet
|
|
328
|
-
` -
|
|
329
|
-
` - Check it: ${frontendUrl(args)} or https://github.com/settings/installations\n` +
|
|
330
|
-
` - Then re-run '${cliInvocation()} github'.\n`
|
|
330
|
+
`Nothing from GitHub yet. Finish the install in the browser — it records itself.\n` +
|
|
331
|
+
` Then re-run '${cliInvocation()} github'.\n`
|
|
331
332
|
);
|
|
332
333
|
}
|
|
333
334
|
|
|
@@ -369,11 +370,9 @@ export async function slackCommand(args) {
|
|
|
369
370
|
* "yes" | "no" | "back" -- back only offered once there is somewhere to go.
|
|
370
371
|
*
|
|
371
372
|
* `--yes` takes each step's own default rather than answering yes to all of
|
|
372
|
-
* them
|
|
373
|
-
* defaults to
|
|
374
|
-
*
|
|
375
|
-
* someone who only meant "stop asking me questions". They have
|
|
376
|
-
* `preman install-desktop` when they want it.
|
|
373
|
+
* them, so a step that should not ride in on a keypress can say so. Every step
|
|
374
|
+
* that asks defaults to yes today; the desktop app, which used to be the
|
|
375
|
+
* exception, no longer asks at all.
|
|
377
376
|
*/
|
|
378
377
|
async function askStep(question, { assumeYes, canGoBack, defaultYes = true }) {
|
|
379
378
|
const fallback = defaultYes ? "yes" : "no";
|
|
@@ -400,7 +399,8 @@ async function askStep(question, { assumeYes, canGoBack, defaultYes = true }) {
|
|
|
400
399
|
* halfway had a desktop app pointed at an empty account. Endpoints come first
|
|
401
400
|
* now because they are the only step that shows what PreMan is for, and every
|
|
402
401
|
* ask after it can name what it just found. The app goes last because it is the
|
|
403
|
-
*
|
|
402
|
+
* only step that asks nothing: by then every decision is made, and a download is
|
|
403
|
+
* something to leave running, not something to make someone watch first.
|
|
404
404
|
*
|
|
405
405
|
* The steps themselves live in connect/guide.js, which is also what
|
|
406
406
|
* `preman connect --guide` runs. Two walks that asked the same questions in
|
|
@@ -416,12 +416,24 @@ export async function onboardCommand(
|
|
|
416
416
|
// Imported at call time, not at the top: guide.js imports this module for MARK
|
|
417
417
|
// and the three integration commands, so a static edge back would close the
|
|
418
418
|
// cycle. By the time anyone runs onboard, this module is fully evaluated.
|
|
419
|
-
const { desktopPrecheck,
|
|
420
|
-
|
|
419
|
+
const { desktopPrecheck, discoverEndpoints, runDesktopInstall } = await import(
|
|
420
|
+
"./connect/guide.js"
|
|
421
|
+
);
|
|
421
422
|
const { resolveAgentToDrive } = await import("./connect/agents.js");
|
|
422
423
|
|
|
423
424
|
process.stdout.write("PreMan setup\n\n");
|
|
424
425
|
|
|
426
|
+
// Which PreMan this is talking to is invisible until the very last line,
|
|
427
|
+
// and a stored login from a local run silently redirects the whole walk:
|
|
428
|
+
// the account is created in a database nobody else can see, and the GitHub
|
|
429
|
+
// App redirects to the real callback, so the install never arrives and the
|
|
430
|
+
// wait looks like GitHub being slow. Production says nothing, because there
|
|
431
|
+
// is nothing to warn about; anything else is worth one line up front.
|
|
432
|
+
const target = backendUrl(args);
|
|
433
|
+
if (target !== DEFAULT_BACKEND) {
|
|
434
|
+
process.stdout.write(`${MARK.skip()} Using ${target}, not ${DEFAULT_BACKEND}.\n\n`);
|
|
435
|
+
}
|
|
436
|
+
|
|
425
437
|
const creds = await authenticateTerminal(args);
|
|
426
438
|
connected(`Signed in as ${creds.user_email || "your account"}.`);
|
|
427
439
|
|
|
@@ -449,7 +461,9 @@ export async function onboardCommand(
|
|
|
449
461
|
agent: args.value("--agent", ""),
|
|
450
462
|
interactive: false,
|
|
451
463
|
});
|
|
452
|
-
const counts = await discoverEndpoints(args, agent, args.value("--name", "preman")
|
|
464
|
+
const counts = await discoverEndpoints(args, agent, args.value("--name", "preman"), {
|
|
465
|
+
handOffBrief: false,
|
|
466
|
+
});
|
|
453
467
|
return { state: counts.registered ? "done" : "skipped", counts };
|
|
454
468
|
},
|
|
455
469
|
},
|
|
@@ -483,9 +497,7 @@ export async function onboardCommand(
|
|
|
483
497
|
return;
|
|
484
498
|
}
|
|
485
499
|
process.stdout.write(
|
|
486
|
-
`${checkout.slug} is still not shared
|
|
487
|
-
` Add it at https://github.com/settings/installations, then re-run ` +
|
|
488
|
-
`'${cliInvocation()} github'.\n`
|
|
500
|
+
`${checkout.slug} is still not shared. Add it at ${INSTALLATIONS_URL}.\n`
|
|
489
501
|
);
|
|
490
502
|
},
|
|
491
503
|
});
|
|
@@ -549,10 +561,10 @@ export async function onboardCommand(
|
|
|
549
561
|
},
|
|
550
562
|
});
|
|
551
563
|
|
|
552
|
-
// Last,
|
|
553
|
-
//
|
|
554
|
-
//
|
|
555
|
-
//
|
|
564
|
+
// Last, and only where there is something to install. The guards run out here
|
|
565
|
+
// rather than inside the step so a Linux user does not get a macOS-only step
|
|
566
|
+
// and someone who already has the app does not watch it download again — both
|
|
567
|
+
// of which happened when the check came after.
|
|
556
568
|
const desktop = desktopPrecheck(args);
|
|
557
569
|
if (desktop?.state === "installed") {
|
|
558
570
|
steps.push({
|
|
@@ -564,11 +576,8 @@ export async function onboardCommand(
|
|
|
564
576
|
} else if (!desktop) {
|
|
565
577
|
steps.push({
|
|
566
578
|
name: "Desktop app",
|
|
567
|
-
question
|
|
568
|
-
// `--desktop` is
|
|
569
|
-
// step its own default and this step's default is no.
|
|
570
|
-
defaultYes: args.has("--desktop"),
|
|
571
|
-
onSkip: desktopSkipped,
|
|
579
|
+
// No question. See offerDesktop in connect/guide.js for why the ask went
|
|
580
|
+
// away; `--no-desktop` is the way past it.
|
|
572
581
|
run: () =>
|
|
573
582
|
runDesktopInstall(args, {
|
|
574
583
|
// Onboarding installs *and* hands the fresh session over, so the app
|
|
@@ -685,16 +694,17 @@ export const INTEGRATIONS_HELP = `
|
|
|
685
694
|
Setup options:
|
|
686
695
|
preman onboard Sign in, map this repo's endpoints, connect it for
|
|
687
696
|
pull requests, and check the endpoints you touch on
|
|
688
|
-
every git push.
|
|
697
|
+
every git push. Installs the desktop app at the end
|
|
689
698
|
preman aws Connect an AWS account and stream a log group
|
|
690
699
|
preman github Install the PreMan GitHub App
|
|
691
700
|
preman slack Add PreMan to a Slack workspace
|
|
692
701
|
|
|
693
|
-
--yes Take each step's default without prompting
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
--desktop
|
|
697
|
-
|
|
702
|
+
--yes Take each step's default without prompting (onboard)
|
|
703
|
+
--desktop Install the desktop app even where there is no macOS
|
|
704
|
+
build, to print the download link (onboard)
|
|
705
|
+
--no-desktop Skip the desktop app, which otherwise installs
|
|
706
|
+
(onboard)
|
|
707
|
+
PREMAN_NO_DESKTOP=1 The same, for runs that cannot pass a flag
|
|
698
708
|
b at any onboard prompt Go back to the previous step
|
|
699
709
|
--account <id> AWS account id, skips the prompt
|
|
700
710
|
--region <region> AWS region for log groups. Defaults to us-east-1
|