@fias/plugin-dev-harness 1.12.0 → 1.12.1

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.
@@ -704,3 +704,124 @@ body {
704
704
  font-weight: 500;
705
705
  margin-bottom: 8px;
706
706
  }
707
+
708
+ /* Sign-in button + paste-code login modal */
709
+ .btn-login {
710
+ background: #2563eb;
711
+ border-color: #1d4ed8;
712
+ color: #fff;
713
+ font-weight: 600;
714
+ }
715
+
716
+ .btn-login:hover {
717
+ background: #1d4ed8;
718
+ }
719
+
720
+ .btn-login.authed {
721
+ background: #16a34a;
722
+ border-color: #15803d;
723
+ }
724
+
725
+ .login-modal {
726
+ position: fixed;
727
+ inset: 0;
728
+ background: rgba(0, 0, 0, 0.6);
729
+ display: flex;
730
+ align-items: center;
731
+ justify-content: center;
732
+ z-index: 1000;
733
+ }
734
+
735
+ .login-modal.hidden {
736
+ display: none;
737
+ }
738
+
739
+ .login-modal-card {
740
+ background: #1f1f23;
741
+ border: 1px solid #3f3f46;
742
+ border-radius: 8px;
743
+ padding: 20px 24px;
744
+ max-width: 480px;
745
+ width: 92%;
746
+ color: #e4e4e7;
747
+ font-size: 13px;
748
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
749
+ }
750
+
751
+ .login-modal-card h2 {
752
+ font-size: 16px;
753
+ margin-bottom: 12px;
754
+ }
755
+
756
+ .login-modal-step {
757
+ margin: 14px 0 6px;
758
+ font-size: 12px;
759
+ color: #a1a1aa;
760
+ }
761
+
762
+ .login-modal-row {
763
+ display: flex;
764
+ gap: 8px;
765
+ align-items: center;
766
+ background: #0f0f12;
767
+ border: 1px solid #3f3f46;
768
+ border-radius: 6px;
769
+ padding: 8px 10px;
770
+ }
771
+
772
+ .login-modal-url {
773
+ color: #93c5fd;
774
+ word-break: break-all;
775
+ flex: 1;
776
+ font-family: monospace;
777
+ font-size: 12px;
778
+ text-decoration: none;
779
+ }
780
+
781
+ .login-modal-url:hover {
782
+ text-decoration: underline;
783
+ }
784
+
785
+ .login-modal-code {
786
+ color: #fde047;
787
+ font-family: monospace;
788
+ font-size: 18px;
789
+ font-weight: 700;
790
+ letter-spacing: 0.1em;
791
+ flex: 1;
792
+ }
793
+
794
+ .btn-copy {
795
+ background: #27272a;
796
+ border: 1px solid #3f3f46;
797
+ color: #e4e4e7;
798
+ padding: 4px 10px;
799
+ border-radius: 4px;
800
+ cursor: pointer;
801
+ font-size: 11px;
802
+ }
803
+
804
+ .btn-copy:hover {
805
+ background: #3f3f46;
806
+ }
807
+
808
+ .login-modal-status {
809
+ margin-top: 16px;
810
+ font-size: 12px;
811
+ color: #a1a1aa;
812
+ min-height: 18px;
813
+ }
814
+
815
+ .login-modal-status.success {
816
+ color: #4ade80;
817
+ }
818
+
819
+ .login-modal-status.error {
820
+ color: #fca5a5;
821
+ }
822
+
823
+ .login-modal-actions {
824
+ margin-top: 16px;
825
+ display: flex;
826
+ justify-content: flex-end;
827
+ }
@@ -17,6 +17,7 @@
17
17
  </select>
18
18
  <span id="mode-badge" class="mode-badge mode-mock">MOCK</span>
19
19
  <button id="mode-toggle" class="btn-icon btn-mode-toggle" title="Switch mode">Switch to Live</button>
20
+ <button id="login-btn" class="btn-icon btn-login" title="Sign in to FIAS">Sign In</button>
20
21
  </div>
21
22
  <div class="toolbar-right">
22
23
  <button id="publish-btn" class="btn-publish" title="Publish plugin">Publish</button>
@@ -111,6 +112,25 @@
111
112
  </div>
112
113
  <div class="console-body" id="console-body"></div>
113
114
  </div>
115
+ <div id="login-modal" class="login-modal hidden" role="dialog" aria-modal="true" aria-labelledby="login-modal-title">
116
+ <div class="login-modal-card">
117
+ <h2 id="login-modal-title">Sign in to FIAS</h2>
118
+ <p class="login-modal-step"><strong>1.</strong> Open this URL in your browser:</p>
119
+ <div class="login-modal-row">
120
+ <a id="login-modal-url" class="login-modal-url" href="#" target="_blank" rel="noopener"></a>
121
+ <button class="btn-copy" data-copy="url" title="Copy URL">Copy</button>
122
+ </div>
123
+ <p class="login-modal-step"><strong>2.</strong> Enter this code, then approve:</p>
124
+ <div class="login-modal-row">
125
+ <span id="login-modal-code" class="login-modal-code"></span>
126
+ <button class="btn-copy" data-copy="code" title="Copy code">Copy</button>
127
+ </div>
128
+ <div class="login-modal-status" id="login-modal-status">Waiting for approval&hellip;</div>
129
+ <div class="login-modal-actions">
130
+ <button id="login-modal-cancel" class="btn-icon">Cancel</button>
131
+ </div>
132
+ </div>
133
+ </div>
114
134
  <script src="/static/harness.js"></script>
115
135
  </body>
116
136
  </html>
@@ -1317,3 +1317,117 @@
1317
1317
  iframe.classList.add('hidden');
1318
1318
  }
1319
1319
  })();
1320
+
1321
+ // Paste-code login flow. Self-contained IIFE — communicates with the harness
1322
+ // server's /api/auth/cli-login/* endpoints, which mint a code, poll the
1323
+ // configured FIAS environment for approval, and persist the returned API key
1324
+ // as the harness's credentials.
1325
+ (function () {
1326
+ 'use strict';
1327
+
1328
+ var loginBtn = document.getElementById('login-btn');
1329
+ var modal = document.getElementById('login-modal');
1330
+ var modalUrl = document.getElementById('login-modal-url');
1331
+ var modalCode = document.getElementById('login-modal-code');
1332
+ var modalStatus = document.getElementById('login-modal-status');
1333
+ var cancelBtn = document.getElementById('login-modal-cancel');
1334
+ if (!loginBtn || !modal) return;
1335
+
1336
+ var pollHandle = null;
1337
+ var pollAbort = null;
1338
+ var currentCode = null;
1339
+
1340
+ function setStatus(text, kind) {
1341
+ modalStatus.textContent = text;
1342
+ modalStatus.className = 'login-modal-status' + (kind ? ' ' + kind : '');
1343
+ }
1344
+
1345
+ function showModal(claimUrl, code) {
1346
+ modalUrl.textContent = claimUrl;
1347
+ modalUrl.href = claimUrl;
1348
+ modalCode.textContent = code;
1349
+ setStatus('Waiting for approval…');
1350
+ modal.classList.remove('hidden');
1351
+ }
1352
+
1353
+ function stopPolling() {
1354
+ if (pollHandle) { clearTimeout(pollHandle); pollHandle = null; }
1355
+ if (pollAbort) { pollAbort.abort(); pollAbort = null; }
1356
+ currentCode = null;
1357
+ }
1358
+
1359
+ function hideModal() {
1360
+ modal.classList.add('hidden');
1361
+ stopPolling();
1362
+ }
1363
+
1364
+ function startLogin() {
1365
+ setStatus('Requesting code…');
1366
+ modal.classList.remove('hidden');
1367
+ fetch('/api/auth/cli-login/start', { method: 'POST' })
1368
+ .then(function (r) {
1369
+ if (!r.ok) return r.json().then(function (e) { throw new Error(e.error || 'Failed to start sign-in'); });
1370
+ return r.json();
1371
+ })
1372
+ .then(function (d) {
1373
+ currentCode = d.code.replace(/[\s-]/g, '').toUpperCase();
1374
+ showModal(d.claimUrl, d.code);
1375
+ var intervalMs = (d.pollIntervalSeconds || 2) * 1000;
1376
+ pollOnce(intervalMs);
1377
+ })
1378
+ .catch(function (e) { setStatus(e.message, 'error'); });
1379
+ }
1380
+
1381
+ function pollOnce(intervalMs) {
1382
+ if (!currentCode) return;
1383
+ pollAbort = new AbortController();
1384
+ fetch('/api/auth/cli-login/poll/' + encodeURIComponent(currentCode), { signal: pollAbort.signal })
1385
+ .then(function (r) {
1386
+ if (r.status === 410) return r.json().then(function () { throw new Error('Code expired. Click Sign In to start over.'); });
1387
+ if (!r.ok) return r.json().then(function (e) { throw new Error(e.error || 'Sign-in poll failed'); });
1388
+ return r.json();
1389
+ })
1390
+ .then(function (d) {
1391
+ if (d.status === 'ready') {
1392
+ setStatus('Signed in.', 'success');
1393
+ loginBtn.textContent = 'Signed In';
1394
+ loginBtn.classList.add('authed');
1395
+ setTimeout(hideModal, 800);
1396
+ return;
1397
+ }
1398
+ pollHandle = setTimeout(function () { pollOnce(intervalMs); }, intervalMs);
1399
+ })
1400
+ .catch(function (e) {
1401
+ if (e.name === 'AbortError') return;
1402
+ setStatus(e.message, 'error');
1403
+ });
1404
+ }
1405
+
1406
+ loginBtn.addEventListener('click', startLogin);
1407
+ cancelBtn.addEventListener('click', hideModal);
1408
+ modal.addEventListener('click', function (ev) {
1409
+ if (ev.target === modal) hideModal();
1410
+ });
1411
+
1412
+ var copyButtons = modal.querySelectorAll('.btn-copy');
1413
+ copyButtons.forEach(function (btn) {
1414
+ btn.addEventListener('click', function () {
1415
+ var which = btn.getAttribute('data-copy');
1416
+ var text = which === 'url' ? modalUrl.textContent : modalCode.textContent;
1417
+ if (navigator.clipboard && navigator.clipboard.writeText) {
1418
+ navigator.clipboard.writeText(text).then(function () {
1419
+ var orig = btn.textContent;
1420
+ btn.textContent = 'Copied!';
1421
+ setTimeout(function () { btn.textContent = orig; }, 1500);
1422
+ });
1423
+ }
1424
+ });
1425
+ });
1426
+
1427
+ fetch('/api/config').then(function (r) { return r.json(); }).then(function (c) {
1428
+ if (c.hasCredentials) {
1429
+ loginBtn.textContent = 'Signed In';
1430
+ loginBtn.classList.add('authed');
1431
+ }
1432
+ }).catch(function () {});
1433
+ })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fias/plugin-dev-harness",
3
- "version": "1.12.0",
3
+ "version": "1.12.1",
4
4
  "description": "Development harness for building and testing FIAS plugin arches locally",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",