@cardstack/boxel-cli 0.9.0-unstable.10 → 0.9.0-unstable.11
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/bundled-realms/base/command.gts +40 -24
- package/bundled-test-harness/assets/{add-workspace-JYBGLgLZ.js → add-workspace-1zRdl8zI.js} +1 -1
- package/bundled-test-harness/assets/{add-workspace-test-DRhnr1r8.js → add-workspace-test-D5iVse00.js} +1 -1
- package/bundled-test-harness/assets/{ai-module-creation-test-BJJhqeeA.js → ai-module-creation-test-GYiPd87c.js} +1 -1
- package/bundled-test-harness/assets/{app-BVQ3DVX4.js → app-CR7E2df0.js} +4 -4
- package/bundled-test-harness/assets/{binary-upload-dedupe-test-Bdmk8qpz.js → binary-upload-dedupe-test-mqTMTOGp.js} +1 -1
- package/bundled-test-harness/assets/{can-invoke-test-C3_ldZpl.js → can-invoke-test-B3LhMm5-.js} +1 -1
- package/bundled-test-harness/assets/{card-chooser-paging-test-C7BNPfWp.js → card-chooser-paging-test-6LwuAKTj.js} +1 -1
- package/bundled-test-harness/assets/{card-chooser-test-D8XNwCaW.js → card-chooser-test-CBvHX1BK.js} +1 -1
- package/bundled-test-harness/assets/{card-def-field-def-relationships-test-CuatVINU.js → card-def-field-def-relationships-test-DodLozx0.js} +1 -1
- package/bundled-test-harness/assets/{card-delete-test-DCmKEZ_Y.js → card-delete-test-BPde9vqy.js} +1 -1
- package/bundled-test-harness/assets/{check-correctness-test-B1bGWVSm.js → check-correctness-test-Ct2aB6aD.js} +1 -1
- package/bundled-test-harness/assets/{choreo-feature-reel-test-CGhD4996.js → choreo-feature-reel-test-Da7hUrQJ.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-BvL1V7P7.js → choreo-gallery-BKVGWFq7.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-build-order-test-vqxi2yVx.js → choreo-gallery-build-order-test-CJMITCCe.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-compositor-test-DhFRJae-.js → choreo-gallery-compositor-test-BVa9Ak-Q.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-cover-test-DmazsR0Z.js → choreo-gallery-cover-test-BFpC-W9i.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-crossing-stage-test-BipANF5u.js → choreo-gallery-crossing-stage-test-BpekNEib.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-demos-alive-test-CDAsZtl-.js → choreo-gallery-demos-alive-test-IOZo64_Z.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-drift-test-EBe6Yaqp.js → choreo-gallery-drift-test-BrPvqI7K.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-escort-test-Ceu7TwiV.js → choreo-gallery-escort-test-BgsIKJio.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-films-test-DrPpbW3l.js → choreo-gallery-films-test-Cnt3fSqM.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-filter-test-CvKk8l6z.js → choreo-gallery-filter-test-uPxHSrLd.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-fold-test-D8X4AXxh.js → choreo-gallery-fold-test-DBtUMaRe.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-guides-test-DWi_cZtu.js → choreo-gallery-guides-test-BDijbyEJ.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-hang-test-Djmr-Jwl.js → choreo-gallery-hang-test-Cfz_f7kq.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-highlight-test-hKhRmfbB.js → choreo-gallery-highlight-test-Bey3u2C9.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-inline-edit-test-DkjthGzn.js → choreo-gallery-inline-edit-test-DWsf_UFF.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-interruption-test-Cy11snG-.js → choreo-gallery-interruption-test-DxgnyIEX.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-lone-stack-test-Dv7sHo4c.js → choreo-gallery-lone-stack-test-CFONDgnZ.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-navigation-test-rXyVsieC.js → choreo-gallery-navigation-test-C3Gx2Wh7.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-playhead-transport-test-DtaCaSLQ.js → choreo-gallery-playhead-transport-test-CH7Jrjhu.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-poplayout-subtree-test-Ap4aNR41.js → choreo-gallery-poplayout-subtree-test-D_WzPJ0V.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-presence-modes-test-BhlW-Lne.js → choreo-gallery-presence-modes-test-Dg9Rjzo9.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-presentation-test-BCkTdHhb.js → choreo-gallery-presentation-test-BwgpOG4i.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-reduced-motion-test-C-i6VxrB.js → choreo-gallery-reduced-motion-test-Bb9vbXwc.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-sheet-standalone-test-YwYop8yo.js → choreo-gallery-sheet-standalone-test-wBtx6YOK.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-sheet-test-BxlTpsW6.js → choreo-gallery-sheet-test-Bgi58xOY.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-site-test-NgXU1y_2.js → choreo-gallery-site-test-0k39S_ID.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-stage-pQUMuoFC.js → choreo-gallery-stage-ZD1b6EcX.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-stage-keyboard-test-BBLXJf7u.js → choreo-gallery-stage-keyboard-test-CwYQ0uLO.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-stages-test-Bpt5h0Ci.js → choreo-gallery-stages-test-D9HzyH3W.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-subdivision-test-DezdmSrj.js → choreo-gallery-subdivision-test-Bqg4OA3x.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-table-plan-test-ClRyzZku.js → choreo-gallery-table-plan-test-kLIQjuGP.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-theater-test-eYpVLcPq.js → choreo-gallery-theater-test-hGflkP4v.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-tuning-test-BLgbgT-x.js → choreo-gallery-tuning-test-DXxSWyWx.js} +1 -1
- package/bundled-test-harness/assets/{choreo-gallery-widget-room-test-7sp5Unfj.js → choreo-gallery-widget-room-test-kDw1KEK8.js} +2 -2
- package/bundled-test-harness/assets/{choreo-gallery-wires-test-gaboWsGB.js → choreo-gallery-wires-test-Ci8pZpzw.js} +1 -1
- package/bundled-test-harness/assets/{codeblocks-test-BfrtgWFf.js → codeblocks-test-CVdMnOCp.js} +1 -1
- package/bundled-test-harness/assets/{codemirror-embed-toolbar-test-Bzke66Ms.js → codemirror-embed-toolbar-test-BssilSQu.js} +1 -1
- package/bundled-test-harness/assets/{compaction-test-vJF6rxRc.js → compaction-test-AMNVQVIq.js} +1 -1
- package/bundled-test-harness/assets/{container-Cg9tfMT-.js → container-aEzZntti.js} +5 -5
- package/bundled-test-harness/assets/{convert-to-class-name-test-GNx5er9G.js → convert-to-class-name-test-D8lm5X8q.js} +1 -1
- package/bundled-test-harness/assets/{create-file-modal-qd-wHuoI.js → create-file-modal-YsrXldcb.js} +1 -1
- package/bundled-test-harness/assets/{create-listing-modal-test-Dg53cvuG.js → create-listing-modal-test-43xjc0SV.js} +1 -1
- package/bundled-test-harness/assets/{credential-fields-test-CUiG7Iez.js → credential-fields-test-BFMDfkwH.js} +1 -1
- package/bundled-test-harness/assets/{debug-message-test-B_dvUaEc.js → debug-message-test-CfuhQ3QC.js} +1 -1
- package/bundled-test-harness/assets/{externals-BnJo838b.js → externals-wVTxNrdG.js} +1 -1
- package/bundled-test-harness/assets/{fallback-banner-live-delete-test-CnZFQWVp.js → fallback-banner-live-delete-test-CLbfPO0a.js} +1 -1
- package/bundled-test-harness/assets/{fallback-banner-test-DTlCNXTq.js → fallback-banner-test-9xbINj2Z.js} +1 -1
- package/bundled-test-harness/assets/{fallback-models-test-DjdqZpc6.js → fallback-models-test-DVQo2MxM.js} +1 -1
- package/bundled-test-harness/assets/{file-attachment-test-CX0j9bZz.js → file-attachment-test-CRKbikdE.js} +1 -1
- package/bundled-test-harness/assets/{general-test-7U7FOyZ1.js → general-test-lSgd8Ss0.js} +1 -1
- package/bundled-test-harness/assets/{index-query-engine-test-Ds1qxgEC.js → index-query-engine-test-BHRXNeAL.js} +1 -1
- package/bundled-test-harness/assets/{inspector-test-Ed3O5tuy.js → inspector-test-C6z8Un9M.js} +1 -1
- package/bundled-test-harness/assets/{instance-filter-matcher-test-D5X1y4EE.js → instance-filter-matcher-test-1Yg4VSaZ.js} +1 -1
- package/bundled-test-harness/assets/{main-CL6WYORU.js → main-Th6TkaXh.js} +1 -1
- package/bundled-test-harness/assets/{markdown-embed-chooser-modal-test-gW6Xfznw.js → markdown-embed-chooser-modal-test-Swm_1E9A.js} +1 -1
- package/bundled-test-harness/assets/{message-tool-test-BpSbgQ6j.js → message-tool-test-lzxIVYt5.js} +1 -1
- package/bundled-test-harness/assets/{modal-BwTd4bsz.js → modal-66tRI_Y_.js} +1 -1
- package/bundled-test-harness/assets/modal-container-CjnDLCtj.js +40 -0
- package/bundled-test-harness/assets/{model-cost-tier-test-CvyrJtBe.js → model-cost-tier-test-Dqd1Ww8g.js} +1 -1
- package/bundled-test-harness/assets/{operation-lowering-test-Dl-IR159.js → operation-lowering-test-BNFo0ZMa.js} +1 -1
- package/bundled-test-harness/assets/{operator-mode-card-chooser-test-DNB9UUeb.js → operator-mode-card-chooser-test-D3hTWkk0.js} +1 -1
- package/bundled-test-harness/assets/{operator-mode-links-test-a2ELMv7x.js → operator-mode-links-test-B6yda5il.js} +1 -1
- package/bundled-test-harness/assets/{operator-mode-test-BOILKxRt.js → operator-mode-test-wj2uYBuS.js} +1 -1
- package/bundled-test-harness/assets/{operator-mode-ui-test-DrYx32gG.js → operator-mode-ui-test-CO7CyFKM.js} +1 -1
- package/bundled-test-harness/assets/{overlay-menu-items-test-B15JkLWi.js → overlay-menu-items-test-C2GA814r.js} +1 -1
- package/bundled-test-harness/assets/{past-sessions-test-Db7VnDiY.js → past-sessions-test-CJyYYcCJ.js} +1 -1
- package/bundled-test-harness/assets/{permission-checking-realm-test-Bf9py4pK.js → permission-checking-realm-test-8dG7pbV2.js} +1 -1
- package/bundled-test-harness/assets/{profile-settings-modal-r1hvskUn.js → profile-settings-modal-BItESTfo.js} +1 -1
- package/bundled-test-harness/assets/{realm-config-policy-standing-test-BiJAwdmt.js → realm-config-policy-standing-test-CpvCRyQD.js} +1 -1
- package/bundled-test-harness/assets/{realm-config-routing-rule-edit-test-CGTMRnZC.js → realm-config-routing-rule-edit-test-hJ-6xFqS.js} +1 -1
- package/bundled-test-harness/assets/{realm-config-settings-test-CMvriHQ1.js → realm-config-settings-test-CJsbxe8c.js} +1 -1
- package/bundled-test-harness/assets/{reasoning-test-B4_QXPYj.js → reasoning-test-DZdScSB0.js} +1 -1
- package/bundled-test-harness/assets/{room-message-test-D0zWbBl2.js → room-message-test-C37ua8-g.js} +1 -1
- package/bundled-test-harness/assets/{run-realm-code-BCbAj6ZU.js → run-realm-code-WsTalbNy.js} +3 -1
- package/bundled-test-harness/assets/{run-realm-code-test-C1SstTFn.js → run-realm-code-test-BUEmsWe8.js} +9 -1
- package/bundled-test-harness/assets/{schema-generation-test-CPSyEnFn.js → schema-generation-test-DDnSXIhb.js} +1 -1
- package/bundled-test-harness/assets/{scrolling-test-8RITl4Yy.js → scrolling-test-B2X_cmFO.js} +1 -1
- package/bundled-test-harness/assets/{searchable-option-test-wy6MhR17.js → searchable-option-test-D8JVkmy0.js} +1 -1
- package/bundled-test-harness/assets/{sending-test-DECmMY11.js → sending-test-Dpz8twhM.js} +1 -1
- package/bundled-test-harness/assets/{skills-test-bnEF18f-.js → skills-test-ozjqNJg9.js} +1 -1
- package/bundled-test-harness/assets/{store-test-4HQ25OC9.js → store-test-BQ9i9Kr5.js} +39 -4
- package/bundled-test-harness/assets/{tests-4eXqEcYs.js → tests-CLX9KyWy.js} +450 -450
- package/bundled-test-harness/assets/{text-input-validator-test-DbbmKb6a.js → text-input-validator-test-Bst2QdAc.js} +1 -1
- package/bundled-test-harness/assets/{to-device-streaming-test-BFMz5Glx.js → to-device-streaming-test-CRPRebs2.js} +1 -1
- package/bundled-test-harness/assets/{token-usage-test-BEZ3LAKu.js → token-usage-test-BhABlxuQ.js} +1 -1
- package/bundled-test-harness/assets/{tools-D-uShEr3.js → tools-BIhB57BB.js} +1 -1
- package/bundled-test-harness/assets/{tools-test-DKOoqRwb.js → tools-test-26_cJBw_.js} +42 -22
- package/bundled-test-harness/assets/{workspace-chooser-Cu2Ytanf.js → workspace-chooser-BoxgLo8E.js} +2 -2
- package/bundled-test-harness/assets/{workspace-chooser-catalog-order-test-CvmdsPdO.js → workspace-chooser-catalog-order-test-CVmYeH4c.js} +1 -1
- package/bundled-test-harness/assets/{workspace-chooser-unreachable-notice-test-DQU5jnd9.js → workspace-chooser-unreachable-notice-test-CMRlJ4q2.js} +1 -1
- package/bundled-test-harness/assets/{workspace-delete-summary-test-CQGv5NBQ.js → workspace-delete-summary-test-Bw91K51c.js} +1 -1
- package/bundled-test-harness/assets/{workspace-UMec6T3x.js → workspace-rCACd5qW.js} +1 -1
- package/bundled-test-harness/assets/{workspace-test-X4Kzx0qN.js → workspace-test-B1wFPgPU.js} +23 -2
- package/bundled-test-harness/assets/{workspace-timestamp-labels-test-CSow2dI6.js → workspace-timestamp-labels-test-CvFFMtct.js} +1 -1
- package/bundled-test-harness/index.html +14 -14
- package/bundled-test-harness/tests/index.html +14 -14
- package/bundled-types/base/command.d.ts +1 -0
- package/bundled-types/host-app/components/modal-container.gts +1 -0
- package/bundled-types/host-app/tools/run-realm-code.ts +6 -2
- package/dist/index.js +269 -269
- package/package.json +1 -1
- package/src/commands/profile.ts +87 -19
- package/src/index.ts +13 -1
- package/src/lib/prompt.ts +54 -6
- package/bundled-test-harness/assets/modal-container-YpGh-tkA.js +0 -40
package/package.json
CHANGED
package/src/commands/profile.ts
CHANGED
|
@@ -422,16 +422,36 @@ export async function listProfiles(
|
|
|
422
422
|
}
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
// What adding a profile reaches beyond this module. Tests stand in for the
|
|
426
|
+
// browser and for the person at the terminal through it.
|
|
427
|
+
export interface AddProfileDeps {
|
|
428
|
+
browserLogin?: typeof browserLogin;
|
|
429
|
+
ensurePersonalRealm?: typeof ensurePersonalRealm;
|
|
430
|
+
prompt?: (question: string) => Promise<string>;
|
|
431
|
+
// Whether a person is at the terminal to answer questions. A coding agent or
|
|
432
|
+
// a script runs the CLI without a TTY, and a question asked there is never
|
|
433
|
+
// answered.
|
|
434
|
+
isTty?: () => boolean;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
interface AddProfileContext {
|
|
438
|
+
browserLogin: typeof browserLogin;
|
|
439
|
+
ensurePersonalRealm: typeof ensurePersonalRealm;
|
|
440
|
+
prompt: (question: string) => Promise<string>;
|
|
441
|
+
interactive: boolean;
|
|
442
|
+
}
|
|
443
|
+
|
|
425
444
|
// Returns false when the user declined to replace an existing profile.
|
|
426
445
|
async function confirmOverwrite(
|
|
427
446
|
manager: ProfileManager,
|
|
428
447
|
matrixId: string,
|
|
448
|
+
ctx: AddProfileContext,
|
|
429
449
|
): Promise<boolean> {
|
|
430
450
|
if (!manager.getProfile(matrixId)) {
|
|
431
451
|
return true;
|
|
432
452
|
}
|
|
433
453
|
console.log(`\n${FG_YELLOW}Profile ${matrixId} already exists.${RESET}`);
|
|
434
|
-
const overwrite = await prompt('Overwrite? [y/N]: ');
|
|
454
|
+
const overwrite = await ctx.prompt('Overwrite? [y/N]: ');
|
|
435
455
|
if (overwrite.toLowerCase() !== 'y') {
|
|
436
456
|
console.log('Cancelled.');
|
|
437
457
|
return false;
|
|
@@ -439,9 +459,12 @@ async function confirmOverwrite(
|
|
|
439
459
|
return true;
|
|
440
460
|
}
|
|
441
461
|
|
|
442
|
-
async function promptDisplayName(
|
|
462
|
+
async function promptDisplayName(
|
|
463
|
+
matrixId: string,
|
|
464
|
+
ctx: AddProfileContext,
|
|
465
|
+
): Promise<string> {
|
|
443
466
|
const defaultDisplayName = `${getUsernameFromMatrixId(matrixId)} \u00b7 ${getDomainFromMatrixId(matrixId)}`;
|
|
444
|
-
const displayNameInput = await prompt(
|
|
467
|
+
const displayNameInput = await ctx.prompt(
|
|
445
468
|
`Display name [${defaultDisplayName}]: `,
|
|
446
469
|
);
|
|
447
470
|
return displayNameInput || defaultDisplayName;
|
|
@@ -458,15 +481,25 @@ type AddProfileOutcome =
|
|
|
458
481
|
// Browser sign-in. The authorization page offers both a password form and a
|
|
459
482
|
// Google button, and reports back whichever account the user signed in as — so
|
|
460
483
|
// unlike the terminal password path there is nothing to ask for up front.
|
|
484
|
+
//
|
|
485
|
+
// Without a TTY the questions that follow the sign-in are not asked: the
|
|
486
|
+
// person already chose to sign in, in the browser, and a question nobody can
|
|
487
|
+
// answer leaves the profile unsaved — so whoever ran the command runs it
|
|
488
|
+
// again and the person is sent through the browser again. The profile is
|
|
489
|
+
// saved as the defaults would have it, and a profile already stored for the
|
|
490
|
+
// account is replaced, since the browser has just proven it is that account.
|
|
491
|
+
// It keeps its display name. `addProfile` leaves a different active profile
|
|
492
|
+
// active.
|
|
461
493
|
async function addProfileViaBrowser(
|
|
462
494
|
manager: ProfileManager,
|
|
463
495
|
matrixUrl: string,
|
|
464
496
|
hostUrl: string,
|
|
465
497
|
realmServerUrl: string,
|
|
498
|
+
ctx: AddProfileContext,
|
|
466
499
|
): Promise<AddProfileOutcome> {
|
|
467
500
|
let auth;
|
|
468
501
|
try {
|
|
469
|
-
auth = await browserLogin({ matrixUrl, hostUrl });
|
|
502
|
+
auth = await ctx.browserLogin({ matrixUrl, hostUrl });
|
|
470
503
|
} catch (err) {
|
|
471
504
|
if (err instanceof SsoTimeoutError) {
|
|
472
505
|
console.log(`\n${FG_YELLOW}${err.message}${RESET}`);
|
|
@@ -482,17 +515,23 @@ async function addProfileViaBrowser(
|
|
|
482
515
|
console.log(
|
|
483
516
|
`\n${FG_GREEN}✓${RESET} Signed in as ${formatProfileBadge(auth.userId)}`,
|
|
484
517
|
);
|
|
485
|
-
|
|
486
|
-
if (
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
518
|
+
let displayName: string | undefined;
|
|
519
|
+
if (ctx.interactive) {
|
|
520
|
+
const proceed = await ctx.prompt('Save this profile? [Y/n]: ');
|
|
521
|
+
if (proceed.toLowerCase() === 'n') {
|
|
522
|
+
console.log('Cancelled.');
|
|
523
|
+
return { status: 'cancelled' };
|
|
524
|
+
}
|
|
490
525
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
526
|
+
if (!(await confirmOverwrite(manager, auth.userId, ctx))) {
|
|
527
|
+
return { status: 'cancelled' };
|
|
528
|
+
}
|
|
494
529
|
|
|
495
|
-
|
|
530
|
+
displayName = await promptDisplayName(auth.userId, ctx);
|
|
531
|
+
} else {
|
|
532
|
+
// Undefined for a new profile, which addProfileWithAuth names by default.
|
|
533
|
+
displayName = manager.getProfile(auth.userId)?.displayName;
|
|
534
|
+
}
|
|
496
535
|
await manager.addProfileWithAuth(
|
|
497
536
|
auth.userId,
|
|
498
537
|
auth,
|
|
@@ -508,7 +547,7 @@ async function addProfileViaBrowser(
|
|
|
508
547
|
// and a failure only warns: the profile works without it, and signing in to
|
|
509
548
|
// the web app offers workspace creation.
|
|
510
549
|
try {
|
|
511
|
-
const result = await ensurePersonalRealm(auth, realmServerUrl);
|
|
550
|
+
const result = await ctx.ensurePersonalRealm(auth, realmServerUrl);
|
|
512
551
|
if (result.outcome === 'created') {
|
|
513
552
|
console.log(
|
|
514
553
|
`${FG_GREEN}✓${RESET} Created your personal workspace: ${FG_CYAN}${result.realmUrl}${RESET}`,
|
|
@@ -537,10 +576,11 @@ async function addProfileViaPassword(
|
|
|
537
576
|
domain: string,
|
|
538
577
|
matrixUrl: string,
|
|
539
578
|
realmServerUrl: string,
|
|
579
|
+
ctx: AddProfileContext,
|
|
540
580
|
): Promise<AddProfileOutcome> {
|
|
541
581
|
console.log(`\nEnter your Boxel username (without @ or domain)`);
|
|
542
582
|
console.log(`${DIM}Example: ctse, aallen90${RESET}`);
|
|
543
|
-
const username = await prompt('Username: ');
|
|
583
|
+
const username = await ctx.prompt('Username: ');
|
|
544
584
|
|
|
545
585
|
if (!username) {
|
|
546
586
|
console.error(`${FG_RED}Error:${RESET} Username is required.`);
|
|
@@ -549,7 +589,7 @@ async function addProfileViaPassword(
|
|
|
549
589
|
|
|
550
590
|
const matrixId = `@${username}:${domain}`;
|
|
551
591
|
|
|
552
|
-
if (!(await confirmOverwrite(manager, matrixId))) {
|
|
592
|
+
if (!(await confirmOverwrite(manager, matrixId, ctx))) {
|
|
553
593
|
return { status: 'cancelled' };
|
|
554
594
|
}
|
|
555
595
|
|
|
@@ -560,7 +600,7 @@ async function addProfileViaPassword(
|
|
|
560
600
|
process.exit(1);
|
|
561
601
|
}
|
|
562
602
|
|
|
563
|
-
const displayName = await promptDisplayName(matrixId);
|
|
603
|
+
const displayName = await promptDisplayName(matrixId, ctx);
|
|
564
604
|
|
|
565
605
|
await manager.addProfile(
|
|
566
606
|
matrixId,
|
|
@@ -572,11 +612,18 @@ async function addProfileViaPassword(
|
|
|
572
612
|
return { status: 'added', matrixId };
|
|
573
613
|
}
|
|
574
614
|
|
|
575
|
-
async function addProfile(
|
|
615
|
+
export async function addProfile(
|
|
576
616
|
manager: ProfileManager,
|
|
577
617
|
environment: EnvironmentDefaults,
|
|
578
618
|
useBrowser = true,
|
|
619
|
+
deps: AddProfileDeps = {},
|
|
579
620
|
): Promise<void> {
|
|
621
|
+
const ctx: AddProfileContext = {
|
|
622
|
+
browserLogin: deps.browserLogin ?? browserLogin,
|
|
623
|
+
ensurePersonalRealm: deps.ensurePersonalRealm ?? ensurePersonalRealm,
|
|
624
|
+
prompt: deps.prompt ?? prompt,
|
|
625
|
+
interactive: (deps.isTty ?? (() => Boolean(process.stdin.isTTY)))(),
|
|
626
|
+
};
|
|
580
627
|
console.log(`\n${BOLD}Add New Profile${RESET}\n`);
|
|
581
628
|
// The environment comes from flags rather than a prompt, so name it. The
|
|
582
629
|
// realm server identifies it — that's the host every later command talks to,
|
|
@@ -595,15 +642,28 @@ async function addProfile(
|
|
|
595
642
|
environment.matrixUrl,
|
|
596
643
|
environment.appUrl ?? environment.realmServerUrl,
|
|
597
644
|
environment.realmServerUrl,
|
|
645
|
+
ctx,
|
|
598
646
|
)
|
|
599
647
|
: { status: 'usePassword' };
|
|
600
648
|
|
|
601
649
|
if (outcome.status === 'usePassword') {
|
|
650
|
+
// The terminal password sign-in is a run of questions, so without a TTY
|
|
651
|
+
// it would wait on an answer that never comes.
|
|
652
|
+
if (!ctx.interactive) {
|
|
653
|
+
console.error(
|
|
654
|
+
`${FG_RED}Error:${RESET} Signing in with a password here needs a terminal. ` +
|
|
655
|
+
`Without one, pass the Matrix ID and the password in the environment: ` +
|
|
656
|
+
`BOXEL_PASSWORD=... boxel profile add -u @<username>:${environment.domain}`,
|
|
657
|
+
);
|
|
658
|
+
process.exitCode = 1;
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
602
661
|
outcome = await addProfileViaPassword(
|
|
603
662
|
manager,
|
|
604
663
|
environment.domain,
|
|
605
664
|
environment.matrixUrl,
|
|
606
665
|
environment.realmServerUrl,
|
|
666
|
+
ctx,
|
|
607
667
|
);
|
|
608
668
|
}
|
|
609
669
|
|
|
@@ -619,8 +679,16 @@ async function addProfile(
|
|
|
619
679
|
|
|
620
680
|
if (manager.getActiveProfileId() === matrixId) {
|
|
621
681
|
console.log(`${DIM}This profile is now active.${RESET}`);
|
|
682
|
+
} else if (!ctx.interactive) {
|
|
683
|
+
// The browser can sign in as an account nobody meant — a Google identity
|
|
684
|
+
// with no matching account gets a new one — and without a TTY nobody has
|
|
685
|
+
// confirmed this one. The profile that was active stays active until
|
|
686
|
+
// someone chooses this one.
|
|
687
|
+
console.log(
|
|
688
|
+
`${DIM}Use 'boxel profile switch ${matrixId}' to switch to this profile.${RESET}`,
|
|
689
|
+
);
|
|
622
690
|
} else {
|
|
623
|
-
const switchNow = await prompt('Switch to this profile now? [Y/n]: ');
|
|
691
|
+
const switchNow = await ctx.prompt('Switch to this profile now? [Y/n]: ');
|
|
624
692
|
if (switchNow.toLowerCase() !== 'n') {
|
|
625
693
|
manager.switchProfile(matrixId);
|
|
626
694
|
console.log(
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { readFileSync } from 'fs';
|
|
|
3
3
|
import { resolve } from 'path';
|
|
4
4
|
import { buildBoxelProgram } from './build-program.ts';
|
|
5
5
|
import { setQuiet } from './lib/cli-log.ts';
|
|
6
|
+
import { FG_RED, RESET } from './lib/colors.ts';
|
|
7
|
+
import { PromptInputClosedError } from './lib/prompt.ts';
|
|
6
8
|
|
|
7
9
|
const pkg = JSON.parse(
|
|
8
10
|
readFileSync(resolve(import.meta.dirname, '../package.json'), 'utf-8'),
|
|
@@ -22,4 +24,14 @@ if (process.argv.includes('--quiet')) {
|
|
|
22
24
|
setQuiet(true);
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
buildBoxelProgram(pkg.version)
|
|
27
|
+
buildBoxelProgram(pkg.version)
|
|
28
|
+
.parseAsync()
|
|
29
|
+
.catch((err: unknown) => {
|
|
30
|
+
if (err instanceof PromptInputClosedError) {
|
|
31
|
+
console.error(`${FG_RED}Error:${RESET} ${err.message}`);
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// Anything else is unexpected, and its stack is what explains it.
|
|
36
|
+
throw err;
|
|
37
|
+
});
|
package/src/lib/prompt.ts
CHANGED
|
@@ -1,22 +1,70 @@
|
|
|
1
1
|
import * as readline from 'readline';
|
|
2
2
|
import { Writable } from 'stream';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// A question whose input ended before it was answered. The CLI's entry point
|
|
5
|
+
// prints its message without a stack, since it describes how the command was
|
|
6
|
+
// run rather than a fault in it.
|
|
7
|
+
export class PromptInputClosedError extends Error {
|
|
8
|
+
constructor(question: string, fromTerminal: boolean) {
|
|
9
|
+
super(
|
|
10
|
+
`No answer to "${question.trim()}".` +
|
|
11
|
+
(fromTerminal
|
|
12
|
+
? ''
|
|
13
|
+
: ' Input ended before one was given. Run the command in a terminal, ' +
|
|
14
|
+
'or pass what it asks for as flags or environment variables.'),
|
|
15
|
+
);
|
|
16
|
+
this.name = 'PromptInputClosedError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface PromptOptions {
|
|
21
|
+
/** @internal The streams to read from and write to; tests pass their own. */
|
|
22
|
+
input?: NodeJS.ReadableStream & {
|
|
23
|
+
readableFlowing?: boolean | null;
|
|
24
|
+
isTTY?: boolean;
|
|
25
|
+
};
|
|
26
|
+
output?: NodeJS.WritableStream;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function prompt(
|
|
30
|
+
question: string,
|
|
31
|
+
options: PromptOptions = {},
|
|
32
|
+
): Promise<string> {
|
|
33
|
+
const input = options.input ?? process.stdin;
|
|
34
|
+
const wasFlowing = input.readableFlowing;
|
|
6
35
|
const rl = readline.createInterface({
|
|
7
|
-
input
|
|
8
|
-
output: process.stdout,
|
|
36
|
+
input,
|
|
37
|
+
output: options.output ?? process.stdout,
|
|
9
38
|
});
|
|
10
39
|
|
|
11
|
-
return new Promise((resolve) => {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
// Input that ends before an answer arrives (stdin from /dev/null, or a pipe
|
|
42
|
+
// that runs dry) closes the interface without ever calling the question's
|
|
43
|
+
// callback. Left alone, the command's promise never settles and the process
|
|
44
|
+
// exits as if it had finished, having done nothing it was asked to.
|
|
45
|
+
let answered = false;
|
|
46
|
+
rl.once('close', () => {
|
|
47
|
+
if (!answered) {
|
|
48
|
+
reject(new PromptInputClosedError(question, Boolean(input.isTTY)));
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// Ctrl+C stops the command, as it does at the no-echo prompt below. Without
|
|
52
|
+
// a listener here readline would close the interface instead, which reads
|
|
53
|
+
// as a question left unanswered.
|
|
54
|
+
rl.on('SIGINT', () => {
|
|
55
|
+
answered = true;
|
|
56
|
+
rl.close();
|
|
57
|
+
process.exit(130);
|
|
58
|
+
});
|
|
12
59
|
rl.question(question, (answer) => {
|
|
60
|
+
answered = true;
|
|
13
61
|
rl.close();
|
|
14
62
|
// Creating the interface resumes stdin, and closing it doesn't undo that —
|
|
15
63
|
// a resumed stdin keeps the event loop alive, so a command whose last act
|
|
16
64
|
// is a prompt would sit there having already finished its work. The
|
|
17
65
|
// no-echo prompt below takes the same care.
|
|
18
66
|
if (!wasFlowing) {
|
|
19
|
-
|
|
67
|
+
input.pause();
|
|
20
68
|
}
|
|
21
69
|
resolve(answer.trim());
|
|
22
70
|
});
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import "./chunk-Rp0pCe6z.js";
|
|
2
|
-
import { wt as setComponentTemplate } from "./cache-DtDz7X5V-Wn8jRDfL.js";
|
|
3
|
-
import { H as templateFactory, S as on } from "./index-DaCZcoCY-CgzbilMy.js";
|
|
4
|
-
import "./modifier-D0EP-FQ7.js";
|
|
5
|
-
import "./component-BJO47HTh.js";
|
|
6
|
-
import { t as Component } from "./dist-CkA1rgqO.js";
|
|
7
|
-
import "./template-factory-ds9s0u8l.js";
|
|
8
|
-
import { $ as CardContainer, D as Header, _ as Modal, nt as Tooltip, pt as IconButton } from "./components-BiwudoSo.js";
|
|
9
|
-
import { v as IconComponent } from "./icons-LMEaS73h.js";
|
|
10
|
-
//#region app/components/modal-container.gts
|
|
11
|
-
var ModalContainer = class extends Component {
|
|
12
|
-
get size() {
|
|
13
|
-
return this.args.size ?? "large";
|
|
14
|
-
}
|
|
15
|
-
get isOpen() {
|
|
16
|
-
return this.args.isOpen ?? true;
|
|
17
|
-
}
|
|
18
|
-
get closeButtonLabel() {
|
|
19
|
-
return this.args.closeButtonLabel ?? "Close";
|
|
20
|
-
}
|
|
21
|
-
static {
|
|
22
|
-
setComponentTemplate(templateFactory({
|
|
23
|
-
"id": "eR3qQR3w",
|
|
24
|
-
"block": "[[[8,[32,0],[[17,1],[24,\"data-scopedcss-adeaf99312-d66846fe00\",\"\"]],[[\"@size\",\"@isOpen\",\"@onClose\",\"@centered\",\"@isOverlayDismissalDisabled\",\"@layer\"],[[30,0,[\"size\"]],[30,0,[\"isOpen\"]],[30,2],[30,3],[30,4],[30,5]]],[[\"default\"],[[[[1,\"\\n \"],[8,[32,1],[[16,0,[29,[\"dialog-box\\n \",[30,6],\"\\n \",[52,[48,[30,9]],\"dialog-box--with-sidebar\"]]]],[24,\"data-scopedcss-adeaf99312-d66846fe00\",\"\"]],[[\"@displayBoundaries\"],[true]],[[\"default\"],[[[[1,\"\\n\"],[41,[48,[30,9]],[[[1,\" \"],[10,\"section\"],[14,0,\"dialog-box__sidebar-header\"],[14,\"data-scopedcss-adeaf99312-d66846fe00\",\"\"],[12],[13],[1,\"\\n \"],[10,\"aside\"],[14,0,\"dialog-box__sidebar\"],[14,\"data-scopedcss-adeaf99312-d66846fe00\",\"\"],[12],[1,\"\\n \"],[18,9,null],[1,\"\\n \"],[13],[1,\"\\n\"]],[]],null],[1,\" \"],[8,[32,2],[[24,0,\"dialog-box__header\"],[24,\"data-scopedcss-adeaf99312-d66846fe00\",\"\"]],[[\"@size\",\"@title\"],[\"large\",[30,7]]],[[\"default\"],[[[[1,\"\\n\"],[41,[30,8],[[[1,\" \"],[8,[32,3],[[24,0,\"dialog-box__close-tooltip\"],[24,\"data-scopedcss-adeaf99312-d66846fe00\",\"\"]],[[\"@placement\",\"@disabled\"],[\"bottom\",[30,4]]],[[\"trigger\",\"content\"],[[[[1,\"\\n \"],[8,[32,4],[[24,0,\"dialog-box__close dialog-box__close--in-tooltip\"],[24,\"aria-label\",\"close modal\"],[16,\"disabled\",[30,4]],[24,\"data-test-close-modal\",\"\"],[24,\"data-scopedcss-adeaf99312-d66846fe00\",\"\"],[4,[32,6],[\"click\",[30,2]],null]],[[\"@icon\",\"@width\",\"@height\"],[[32,5],\"12\",\"12\"]],null],[1,\"\\n \"]],[]],[[[1,\"\\n \"],[10,1],[14,0,\"close-tooltip\"],[14,\"data-scopedcss-adeaf99312-d66846fe00\",\"\"],[12],[1,\"\\n \"],[10,1],[14,0,\"close-tooltip__label\"],[14,\"data-scopedcss-adeaf99312-d66846fe00\",\"\"],[12],[1,[30,0,[\"closeButtonLabel\"]]],[13],[1,\"\\n \"],[10,\"kbd\"],[14,0,\"shortcut-key\"],[14,\"data-scopedcss-adeaf99312-d66846fe00\",\"\"],[12],[1,[30,8]],[13],[1,\"\\n \"],[13],[1,\"\\n \"]],[]]]]],[1,\"\\n\"]],[]],[[[1,\" \"],[8,[32,4],[[24,0,\"dialog-box__close\"],[24,\"aria-label\",\"close modal\"],[16,\"disabled\",[30,4]],[24,\"data-test-close-modal\",\"\"],[24,\"data-scopedcss-adeaf99312-d66846fe00\",\"\"],[4,[32,6],[\"click\",[30,2]],null]],[[\"@icon\",\"@width\",\"@height\"],[[32,5],\"12\",\"12\"]],null],[1,\"\\n\"]],[]]],[1,\" \"],[18,10,null],[1,\"\\n \"]],[]]]]],[1,\"\\n \"],[10,0],[14,0,\"dialog-box__content\"],[14,\"data-scopedcss-adeaf99312-d66846fe00\",\"\"],[12],[1,\"\\n \"],[18,11,null],[1,\"\\n \"],[13],[1,\"\\n\"],[41,[48,[30,12]],[[[1,\" \"],[10,\"footer\"],[14,0,\"dialog-box__footer\"],[14,\"data-scopedcss-adeaf99312-d66846fe00\",\"\"],[12],[1,\"\\n \"],[18,12,null],[1,\"\\n \"],[13],[1,\"\\n\"]],[]],null],[1,\" \"]],[]]]]],[1,\"\\n\"]],[]]]]],[1,\"\\n\"]],[\"&attrs\",\"@onClose\",\"@centered\",\"@isCloseDisabled\",\"@layer\",\"@cardContainerClass\",\"@title\",\"@closeButtonShortcut\",\"&sidebar\",\"&header\",\"&content\",\"&footer\"],[\"if\",\"has-block\",\"yield\"]]",
|
|
25
|
-
"moduleName": "/home/runner/work/boxel/boxel/packages/host/app/components/modal-container.gts",
|
|
26
|
-
"scope": () => [
|
|
27
|
-
Modal,
|
|
28
|
-
CardContainer,
|
|
29
|
-
Header,
|
|
30
|
-
Tooltip,
|
|
31
|
-
IconButton,
|
|
32
|
-
IconComponent,
|
|
33
|
-
on
|
|
34
|
-
],
|
|
35
|
-
"isStrictMode": true
|
|
36
|
-
}), this);
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
//#endregion
|
|
40
|
-
export { ModalContainer as t };
|