fraim-framework 2.0.176 → 2.0.179

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.
@@ -580,8 +580,72 @@
580
580
  }
581
581
  }
582
582
 
583
+ // Issue #646: when first-run was launched without a key (the no-terminal macOS
584
+ // installer path), gate the whole wizard behind a paste-your-key step.
585
+ function renderKeyEntry() {
586
+ CHECKLIST_EL.className = 'setup-shell';
587
+ CHECKLIST_EL.innerHTML = '';
588
+ PRIMARY_BUTTON.style.display = 'none';
589
+ setHeader('Set up FRAIM', 'Paste the FRAIM key from your account page to get started.');
590
+
591
+ const card = document.createElement('div');
592
+ card.className = 'setup-pane';
593
+ card.setAttribute('data-testid', 'key-entry');
594
+
595
+ const label = document.createElement('label');
596
+ label.className = 'pane-copy';
597
+ label.setAttribute('for', 'fraim-key-input');
598
+ label.textContent = 'Your FRAIM key';
599
+ card.appendChild(label);
600
+
601
+ const input = document.createElement('input');
602
+ input.type = 'text';
603
+ input.id = 'fraim-key-input';
604
+ input.className = 'key-input';
605
+ input.placeholder = 'fraim_…';
606
+ input.autocapitalize = 'off';
607
+ input.autocomplete = 'off';
608
+ input.spellcheck = false;
609
+ input.setAttribute('data-testid', 'key-input');
610
+ card.appendChild(input);
611
+
612
+ const err = document.createElement('p');
613
+ err.className = 'locked-note';
614
+ err.setAttribute('data-testid', 'key-error');
615
+ err.hidden = true;
616
+ card.appendChild(err);
617
+
618
+ const submit = button('Continue', 'primary');
619
+ submit.setAttribute('data-testid', 'key-submit');
620
+ const onSubmit = async () => {
621
+ const value = input.value.trim();
622
+ err.hidden = true;
623
+ submit.disabled = true;
624
+ submit.textContent = 'Checking…';
625
+ try {
626
+ const resp = await api('/api/first-run/set-key', 'POST', { key: value });
627
+ state.session = resp.session;
628
+ if (!state.session.state.agentInstalls) state.session.state.agentInstalls = {};
629
+ state.activeStep = chooseActiveStep();
630
+ render();
631
+ } catch (e) {
632
+ err.textContent = e.message || 'That key was not accepted. Copy it again from your account page.';
633
+ err.hidden = false;
634
+ submit.disabled = false;
635
+ submit.textContent = 'Continue';
636
+ }
637
+ };
638
+ submit.addEventListener('click', onSubmit);
639
+ input.addEventListener('keydown', (e) => { if (e.key === 'Enter') onSubmit(); });
640
+ card.appendChild(submit);
641
+
642
+ CHECKLIST_EL.appendChild(card);
643
+ input.focus();
644
+ }
645
+
583
646
  function render() {
584
647
  if (!state.session) return;
648
+ if (state.session.needsKey) { renderKeyEntry(); return; }
585
649
  if (!STEP_ORDER.includes(state.activeStep)) state.activeStep = chooseActiveStep();
586
650
  renderShell((pane) => {
587
651
  if (state.activeStep === 'prereqs') renderPrereqs(pane);
@@ -494,6 +494,20 @@ body {
494
494
  font-size: 14px;
495
495
  }
496
496
  .locked-note { color: var(--warn); }
497
+
498
+ /* Issue #646: paste-your-key step (no-terminal macOS installer path). */
499
+ .key-input {
500
+ width: 100%;
501
+ margin: 10px 0 4px;
502
+ padding: 10px 12px;
503
+ font-family: ui-monospace, 'SFMono-Regular', Menlo, monospace;
504
+ font-size: 14px;
505
+ border: 1px solid var(--border, #2c3343);
506
+ border-radius: 8px;
507
+ background: var(--panel, #12151c);
508
+ color: var(--text, #e6e9ef);
509
+ }
510
+ .key-input:focus-visible { outline: 2px solid var(--accent-strong, #6366f1); outline-offset: 1px; }
497
511
  .row-list {
498
512
  list-style: none;
499
513
  margin: 0;