@displaydev/cli 0.1.1 → 0.2.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.
package/README.md CHANGED
@@ -15,7 +15,7 @@ npm install -g @displaydev/cli
15
15
  dsp login
16
16
 
17
17
  # Or set an API key (from the dashboard)
18
- export DISPLAY_API_KEY=sk_live_...
18
+ export DISPLAYDEV_API_KEY=sk_live_...
19
19
  ```
20
20
 
21
21
  ## Usage
@@ -52,7 +52,7 @@ The CLI doubles as an MCP server over stdio. Add to your MCP client config:
52
52
  "command": "dsp",
53
53
  "args": ["mcp"],
54
54
  "env": {
55
- "DISPLAY_API_KEY": "sk_live_..."
55
+ "DISPLAYDEV_API_KEY": "sk_live_..."
56
56
  }
57
57
  }
58
58
  }
@@ -356,6 +356,113 @@ export var ApiClient = /*#__PURE__*/ function() {
356
356
  }).call(this);
357
357
  }
358
358
  },
359
+ {
360
+ key: "authCheck",
361
+ value: function authCheck(email) {
362
+ return _async_to_generator(function() {
363
+ return _ts_generator(this, function(_state) {
364
+ return [
365
+ 2,
366
+ this.requestNoAuth('POST', '/cli/auth-check', {
367
+ email: email
368
+ })
369
+ ];
370
+ });
371
+ }).call(this);
372
+ }
373
+ },
374
+ {
375
+ key: "requestDeviceCode",
376
+ value: function requestDeviceCode(clientId) {
377
+ return _async_to_generator(function() {
378
+ return _ts_generator(this, function(_state) {
379
+ return [
380
+ 2,
381
+ this.requestNoAuth('POST', '/api/auth/device/code', {
382
+ client_id: clientId
383
+ })
384
+ ];
385
+ });
386
+ }).call(this);
387
+ }
388
+ },
389
+ {
390
+ key: "pollDeviceToken",
391
+ value: function pollDeviceToken(deviceCode, clientId) {
392
+ return _async_to_generator(function() {
393
+ return _ts_generator(this, function(_state) {
394
+ return [
395
+ 2,
396
+ this.requestNoAuthRaw('POST', '/api/auth/device/token', {
397
+ device_code: deviceCode,
398
+ client_id: clientId,
399
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
400
+ })
401
+ ];
402
+ });
403
+ }).call(this);
404
+ }
405
+ },
406
+ {
407
+ key: "validateApiKey",
408
+ value: /**
409
+ * Validate an API key against the API.
410
+ * Returns 'valid', 'invalid' (401/403), or 'network_error'.
411
+ */ function validateApiKey(apiKey) {
412
+ return _async_to_generator(function() {
413
+ var res, unused;
414
+ return _ts_generator(this, function(_state) {
415
+ switch(_state.label){
416
+ case 0:
417
+ _state.trys.push([
418
+ 0,
419
+ 2,
420
+ ,
421
+ 3
422
+ ]);
423
+ return [
424
+ 4,
425
+ fetch("".concat(this.baseUrl, "/v1/artifacts?limit=1"), {
426
+ headers: {
427
+ 'Authorization': "Bearer ".concat(apiKey),
428
+ 'X-Client-Type': this.clientType
429
+ }
430
+ })
431
+ ];
432
+ case 1:
433
+ res = _state.sent();
434
+ return [
435
+ 3,
436
+ 3
437
+ ];
438
+ case 2:
439
+ unused = _state.sent();
440
+ return [
441
+ 2,
442
+ 'network_error'
443
+ ];
444
+ case 3:
445
+ if (res.ok) {
446
+ return [
447
+ 2,
448
+ 'valid'
449
+ ];
450
+ }
451
+ if (res.status === 401 || res.status === 403) {
452
+ return [
453
+ 2,
454
+ 'invalid'
455
+ ];
456
+ }
457
+ return [
458
+ 2,
459
+ 'network_error'
460
+ ];
461
+ }
462
+ });
463
+ }).call(this);
464
+ }
465
+ },
359
466
  {
360
467
  key: "request",
361
468
  value: function request(method, path, body) {
@@ -387,6 +494,37 @@ export var ApiClient = /*#__PURE__*/ function() {
387
494
  }).call(this);
388
495
  }
389
496
  },
497
+ {
498
+ key: "requestNoAuthRaw",
499
+ value: /** Like requestNoAuth but returns parsed JSON without throwing on non-2xx (for polling endpoints). */ function requestNoAuthRaw(method, path, body) {
500
+ return _async_to_generator(function() {
501
+ var url, res;
502
+ return _ts_generator(this, function(_state) {
503
+ switch(_state.label){
504
+ case 0:
505
+ url = "".concat(this.baseUrl).concat(path);
506
+ return [
507
+ 4,
508
+ fetch(url, {
509
+ method: method,
510
+ headers: {
511
+ 'Content-Type': 'application/json',
512
+ 'X-Client-Type': this.clientType
513
+ },
514
+ body: body ? JSON.stringify(body) : undefined
515
+ })
516
+ ];
517
+ case 1:
518
+ res = _state.sent();
519
+ return [
520
+ 2,
521
+ res.json()
522
+ ];
523
+ }
524
+ });
525
+ }).call(this);
526
+ }
527
+ },
390
528
  {
391
529
  key: "doFetch",
392
530
  value: function doFetch(method, path, body, headers) {
package/dist/main.js CHANGED
@@ -28,6 +28,14 @@ function _async_to_generator(fn) {
28
28
  });
29
29
  };
30
30
  }
31
+ function _instanceof(left, right) {
32
+ "@swc/helpers - instanceof";
33
+ if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
34
+ return !!right[Symbol.hasInstance](left);
35
+ } else {
36
+ return left instanceof right;
37
+ }
38
+ }
31
39
  function _ts_generator(thisArg, body) {
32
40
  var f, y, t, _ = {
33
41
  label: 0,
@@ -129,13 +137,17 @@ function _ts_generator(thisArg, body) {
129
137
  }
130
138
  import { readFile, mkdir, writeFile, rename, chmod } from 'node:fs/promises';
131
139
  import { createInterface } from 'node:readline/promises';
132
- import { homedir } from 'node:os';
140
+ import { createRequire } from 'node:module';
141
+ import { execFileSync } from 'node:child_process';
142
+ import { homedir, platform } from 'node:os';
133
143
  import { join, extname } from 'node:path';
134
144
  import { Command } from 'commander';
135
145
  import { ApiClient } from './api-client.js';
136
146
  import { startMcpServer } from './mcp-server.js';
147
+ var require = createRequire(import.meta.url);
148
+ var version = require('../package.json').version;
137
149
  var DEFAULT_API_URL = 'https://api.display.dev';
138
- var CONFIG_DIR = join(homedir(), '.display');
150
+ var CONFIG_DIR = join(homedir(), '.displaydev');
139
151
  var CONFIG_PATH = join(CONFIG_DIR, 'config.json');
140
152
  function loadConfig() {
141
153
  return _async_to_generator(function() {
@@ -216,12 +228,12 @@ function saveConfig(config) {
216
228
  })();
217
229
  }
218
230
  function resolveAuth() {
219
- var apiKey = process.env.DISPLAY_API_KEY;
231
+ var apiKey = process.env.DISPLAYDEV_API_KEY;
220
232
  if (apiKey) {
221
- var _process_env_DISPLAY_API_URL;
233
+ var _process_env_DISPLAYDEV_API_URL;
222
234
  return {
223
235
  apiKey: apiKey,
224
- apiUrl: (_process_env_DISPLAY_API_URL = process.env.DISPLAY_API_URL) !== null && _process_env_DISPLAY_API_URL !== void 0 ? _process_env_DISPLAY_API_URL : DEFAULT_API_URL
236
+ apiUrl: (_process_env_DISPLAYDEV_API_URL = process.env.DISPLAYDEV_API_URL) !== null && _process_env_DISPLAYDEV_API_URL !== void 0 ? _process_env_DISPLAYDEV_API_URL : DEFAULT_API_URL
225
237
  };
226
238
  }
227
239
  return null;
@@ -255,7 +267,7 @@ function resolveAuthOrConfig() {
255
267
  }
256
268
  ];
257
269
  }
258
- console.error('Not authenticated. Set DISPLAY_API_KEY or run: dsp login');
270
+ console.error('Not authenticated. Set DISPLAYDEV_API_KEY or run: dsp login');
259
271
  process.exit(1);
260
272
  return [
261
273
  2
@@ -271,7 +283,7 @@ function createClient(auth) {
271
283
  clientType: 'cli'
272
284
  });
273
285
  }
274
- var program = new Command().name('dsp').description('display.dev CLI — publish artifacts behind company auth').version('0.0.0');
286
+ var program = new Command().name('dsp').description('display.dev CLI — publish artifacts behind company auth').version(version);
275
287
  function collectEmails(value, prev) {
276
288
  return prev.concat(value);
277
289
  }
@@ -471,55 +483,270 @@ program.command('delete <shortId>').description('Delete an artifact permanently'
471
483
  });
472
484
  })();
473
485
  });
486
+ function openBrowser(url) {
487
+ try {
488
+ var os = platform();
489
+ if (os === 'darwin') {
490
+ execFileSync('open', [
491
+ url
492
+ ]);
493
+ } else if (os === 'win32') {
494
+ execFileSync('cmd', [
495
+ '/c',
496
+ 'start',
497
+ '',
498
+ url
499
+ ]);
500
+ } else {
501
+ execFileSync('xdg-open', [
502
+ url
503
+ ]);
504
+ }
505
+ } catch (unused) {
506
+ // Browser opening failed (no display, SSH session, etc.)
507
+ // URL is already printed — user can open it manually
508
+ }
509
+ }
510
+ function sleep(ms) {
511
+ return new Promise(function(resolve) {
512
+ return setTimeout(resolve, ms);
513
+ });
514
+ }
474
515
  // --- login ---
475
- program.command('login').description('Authenticate with display.dev via email OTP').option('--email <email>', 'Email address').action(function(opts) {
516
+ program.command('login').description('Authenticate with display.dev').option('--email <email>', 'Email address').option('--api-key [key]', 'Authenticate with an API key').action(function(opts) {
476
517
  return _async_to_generator(function() {
477
- var _process_env_DISPLAY_API_URL, apiUrl, client, rl, email, code, result;
518
+ var _process_env_DISPLAYDEV_API_URL, apiUrl, client, key, rl, validation, email, rl1, method, check, unused, err, msg, rl2, code, result, unused1, deviceResult, unused2, device_code, verification_uri_complete, initialInterval, interval, tokenResult, unused3, _$err;
478
519
  return _ts_generator(this, function(_state) {
479
520
  switch(_state.label){
480
521
  case 0:
481
- apiUrl = (_process_env_DISPLAY_API_URL = process.env.DISPLAY_API_URL) !== null && _process_env_DISPLAY_API_URL !== void 0 ? _process_env_DISPLAY_API_URL : DEFAULT_API_URL;
522
+ apiUrl = (_process_env_DISPLAYDEV_API_URL = process.env.DISPLAYDEV_API_URL) !== null && _process_env_DISPLAYDEV_API_URL !== void 0 ? _process_env_DISPLAYDEV_API_URL : DEFAULT_API_URL;
482
523
  client = new ApiClient({
483
524
  baseUrl: apiUrl,
484
525
  apiKey: '',
485
526
  clientType: 'cli'
486
527
  });
528
+ if (!(opts.apiKey !== undefined)) return [
529
+ 3,
530
+ 8
531
+ ];
532
+ if (!(typeof opts.apiKey === 'string')) return [
533
+ 3,
534
+ 1
535
+ ];
536
+ key = opts.apiKey;
537
+ return [
538
+ 3,
539
+ 5
540
+ ];
541
+ case 1:
542
+ if (!process.stdin.isTTY) return [
543
+ 3,
544
+ 3
545
+ ];
546
+ // Mask input for interactive API key entry
547
+ process.stdout.write('API key: ');
548
+ return [
549
+ 4,
550
+ new Promise(function(resolve) {
551
+ var input = '';
552
+ process.stdin.setRawMode(true);
553
+ process.stdin.resume();
554
+ process.stdin.setEncoding('utf8');
555
+ var cleanup = function cleanup() {
556
+ process.stdin.setRawMode(false);
557
+ process.stdin.pause();
558
+ process.stdin.removeListener('data', onData);
559
+ };
560
+ var onData = function onData(ch) {
561
+ if (ch === '\r' || ch === '\n') {
562
+ cleanup();
563
+ process.stdout.write('\n');
564
+ resolve(input);
565
+ } else if (ch === '\u0003') {
566
+ cleanup();
567
+ process.stdout.write('\n');
568
+ process.exit(1);
569
+ } else if (ch === '\u007F' || ch === '\b') {
570
+ if (input.length > 0) {
571
+ input = input.slice(0, -1);
572
+ }
573
+ } else {
574
+ input += ch;
575
+ }
576
+ };
577
+ process.stdin.on('data', onData);
578
+ })
579
+ ];
580
+ case 2:
581
+ key = _state.sent();
582
+ return [
583
+ 3,
584
+ 5
585
+ ];
586
+ case 3:
587
+ // Non-TTY: read from stdin (piped input)
487
588
  rl = createInterface({
488
589
  input: process.stdin,
489
590
  output: process.stdout
490
591
  });
592
+ return [
593
+ 4,
594
+ rl.question('API key: ')
595
+ ];
596
+ case 4:
597
+ key = _state.sent();
598
+ rl.close();
599
+ _state.label = 5;
600
+ case 5:
601
+ key = key.trim();
602
+ if (!key) {
603
+ console.error('API key cannot be empty.');
604
+ process.exit(1);
605
+ }
606
+ return [
607
+ 4,
608
+ client.validateApiKey(key)
609
+ ];
610
+ case 6:
611
+ validation = _state.sent();
612
+ if (validation === 'invalid') {
613
+ console.error('Invalid API key. Check the key and try again.');
614
+ process.exit(1);
615
+ }
616
+ if (validation === 'network_error') {
617
+ // Network issue — save anyway, validate on first use
618
+ }
619
+ return [
620
+ 4,
621
+ saveConfig({
622
+ token: key,
623
+ apiUrl: apiUrl
624
+ })
625
+ ];
626
+ case 7:
627
+ _state.sent();
628
+ console.log('Authenticated.');
629
+ return [
630
+ 2
631
+ ];
632
+ case 8:
633
+ // --- Mode 1 & 2: Email-based login ---
491
634
  email = opts.email;
492
635
  if (!!email) return [
493
636
  3,
494
- 2
637
+ 10
495
638
  ];
639
+ if (!process.stdin.isTTY) {
640
+ console.error('Email is required. Use --email <email> for non-interactive mode.');
641
+ process.exit(1);
642
+ }
643
+ rl1 = createInterface({
644
+ input: process.stdin,
645
+ output: process.stdout
646
+ });
496
647
  return [
497
648
  4,
498
- rl.question('Email: ')
649
+ rl1.question('Email: ')
499
650
  ];
500
- case 1:
651
+ case 9:
501
652
  email = _state.sent();
502
- _state.label = 2;
503
- case 2:
653
+ rl1.close();
654
+ _state.label = 10;
655
+ case 10:
656
+ email = email.trim();
657
+ // Check auth method
658
+ method = 'otp';
659
+ _state.label = 11;
660
+ case 11:
661
+ _state.trys.push([
662
+ 11,
663
+ 13,
664
+ ,
665
+ 14
666
+ ]);
667
+ return [
668
+ 4,
669
+ client.authCheck(email)
670
+ ];
671
+ case 12:
672
+ check = _state.sent();
673
+ method = check.method;
674
+ return [
675
+ 3,
676
+ 14
677
+ ];
678
+ case 13:
679
+ unused = _state.sent();
680
+ return [
681
+ 3,
682
+ 14
683
+ ];
684
+ case 14:
685
+ if (!(method === 'otp')) return [
686
+ 3,
687
+ 25
688
+ ];
689
+ _state.label = 15;
690
+ case 15:
691
+ _state.trys.push([
692
+ 15,
693
+ 17,
694
+ ,
695
+ 18
696
+ ]);
504
697
  return [
505
698
  4,
506
699
  client.sendOtp(email)
507
700
  ];
508
- case 3:
701
+ case 16:
509
702
  _state.sent();
703
+ return [
704
+ 3,
705
+ 18
706
+ ];
707
+ case 17:
708
+ err = _state.sent();
709
+ msg = _instanceof(err, Error) ? err.message : 'Something went wrong';
710
+ if (msg.includes('requires SSO')) {
711
+ console.error('Your organization requires SSO. Run dsp login again.');
712
+ } else {
713
+ console.error('Something went wrong. Check your connection and try again.');
714
+ }
715
+ process.exit(1);
716
+ return [
717
+ 3,
718
+ 18
719
+ ];
720
+ case 18:
510
721
  console.log("Code sent to ".concat(email));
722
+ if (!process.stdin.isTTY) {
723
+ console.error('OTP code required. Use dsp login --api-key for non-interactive auth.');
724
+ process.exit(1);
725
+ }
726
+ rl2 = createInterface({
727
+ input: process.stdin,
728
+ output: process.stdout
729
+ });
511
730
  return [
512
731
  4,
513
- rl.question('Enter the 6-digit code: ')
732
+ rl2.question('Enter the 6-digit code: ')
514
733
  ];
515
- case 4:
734
+ case 19:
516
735
  code = _state.sent();
517
- rl.close();
736
+ rl2.close();
737
+ _state.label = 20;
738
+ case 20:
739
+ _state.trys.push([
740
+ 20,
741
+ 23,
742
+ ,
743
+ 24
744
+ ]);
518
745
  return [
519
746
  4,
520
747
  client.verifyOtp(email, code.trim())
521
748
  ];
522
- case 5:
749
+ case 21:
523
750
  result = _state.sent();
524
751
  return [
525
752
  4,
@@ -528,9 +755,155 @@ program.command('login').description('Authenticate with display.dev via email OT
528
755
  apiUrl: apiUrl
529
756
  })
530
757
  ];
531
- case 6:
758
+ case 22:
532
759
  _state.sent();
533
760
  console.log("Logged in as ".concat(email));
761
+ return [
762
+ 3,
763
+ 24
764
+ ];
765
+ case 23:
766
+ unused1 = _state.sent();
767
+ console.error('Invalid or expired code. Try again.');
768
+ process.exit(1);
769
+ return [
770
+ 3,
771
+ 24
772
+ ];
773
+ case 24:
774
+ return [
775
+ 3,
776
+ 38
777
+ ];
778
+ case 25:
779
+ // --- Mode 2: SSO device flow ---
780
+ console.log('Your organization requires SSO. Opening browser...');
781
+ _state.label = 26;
782
+ case 26:
783
+ _state.trys.push([
784
+ 26,
785
+ 28,
786
+ ,
787
+ 29
788
+ ]);
789
+ return [
790
+ 4,
791
+ client.requestDeviceCode('dsp-cli')
792
+ ];
793
+ case 27:
794
+ deviceResult = _state.sent();
795
+ return [
796
+ 3,
797
+ 29
798
+ ];
799
+ case 28:
800
+ unused2 = _state.sent();
801
+ console.error('Something went wrong. Check your connection and try again.');
802
+ process.exit(1);
803
+ return [
804
+ 3,
805
+ 29
806
+ ];
807
+ case 29:
808
+ device_code = deviceResult.device_code, verification_uri_complete = deviceResult.verification_uri_complete, initialInterval = deviceResult.interval;
809
+ console.log();
810
+ console.log(" ".concat(verification_uri_complete));
811
+ console.log();
812
+ openBrowser(verification_uri_complete);
813
+ // Poll for token
814
+ interval = (initialInterval || 5) * 1000;
815
+ process.stdout.write('Waiting for authentication...');
816
+ _state.label = 30;
817
+ case 30:
818
+ if (!true) return [
819
+ 3,
820
+ 38
821
+ ];
822
+ return [
823
+ 4,
824
+ sleep(interval)
825
+ ];
826
+ case 31:
827
+ _state.sent();
828
+ tokenResult = void 0;
829
+ _state.label = 32;
830
+ case 32:
831
+ _state.trys.push([
832
+ 32,
833
+ 34,
834
+ ,
835
+ 35
836
+ ]);
837
+ return [
838
+ 4,
839
+ client.pollDeviceToken(device_code, 'dsp-cli')
840
+ ];
841
+ case 33:
842
+ tokenResult = _state.sent();
843
+ return [
844
+ 3,
845
+ 35
846
+ ];
847
+ case 34:
848
+ unused3 = _state.sent();
849
+ // Transient network error — retry on next interval
850
+ return [
851
+ 3,
852
+ 30
853
+ ];
854
+ case 35:
855
+ if (!('access_token' in tokenResult)) return [
856
+ 3,
857
+ 37
858
+ ];
859
+ console.log(' done');
860
+ return [
861
+ 4,
862
+ saveConfig({
863
+ token: tokenResult.access_token,
864
+ apiUrl: apiUrl
865
+ })
866
+ ];
867
+ case 36:
868
+ _state.sent();
869
+ console.log("Logged in as ".concat(email));
870
+ return [
871
+ 2
872
+ ];
873
+ case 37:
874
+ _$err = tokenResult;
875
+ if (_$err.error === 'authorization_pending') {
876
+ return [
877
+ 3,
878
+ 30
879
+ ];
880
+ }
881
+ if (_$err.error === 'slow_down') {
882
+ interval += 5000;
883
+ return [
884
+ 3,
885
+ 30
886
+ ];
887
+ }
888
+ if (_$err.error === 'access_denied') {
889
+ console.log();
890
+ console.error('Authentication denied. Run dsp login to try again.');
891
+ process.exit(1);
892
+ }
893
+ if (_$err.error === 'expired_token') {
894
+ console.log();
895
+ console.error('Code expired. Run dsp login to try again.');
896
+ process.exit(1);
897
+ }
898
+ // Unknown error
899
+ console.log();
900
+ console.error(_$err.error_description || 'Authentication failed. Run dsp login to try again.');
901
+ process.exit(1);
902
+ return [
903
+ 3,
904
+ 30
905
+ ];
906
+ case 38:
534
907
  return [
535
908
  2
536
909
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@displaydev/cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "dsp": "dist/main.js"