@displaydev/cli 0.2.0 → 0.3.0

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.
Files changed (3) hide show
  1. package/README.md +31 -0
  2. package/dist/main.js +151 -62
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -18,6 +18,37 @@ dsp login
18
18
  export DISPLAYDEV_API_KEY=sk_live_...
19
19
  ```
20
20
 
21
+ ### Agent-native login
22
+
23
+ `dsp login` is designed to be driven entirely by an AI agent that has inbox
24
+ access (Gmail/IMAP via MCP, etc.) — no human-in-the-loop required.
25
+
26
+ The command adapts to its environment:
27
+
28
+ | Invocation | Behavior |
29
+ |---|---|
30
+ | `dsp login --email you@acme.com` **(TTY)** | Sends OTP, prompts for the code in the terminal. |
31
+ | `dsp login --email you@acme.com` **(non-TTY)** | Sends OTP and exits 0. Caller reads the code (e.g. from inbox) and re-invokes. |
32
+ | `dsp login --email you@acme.com --code 123456` | Verifies the code, persists the token, exits. |
33
+
34
+ For structured output that doesn't rely on prose parsing:
35
+
36
+ ```bash
37
+ dsp login --email you@acme.com --json
38
+ # → {"status":"otp_sent","email":"you@acme.com",
39
+ # "next":{"command":"dsp login","args":["--email","you@acme.com","--code","<code>"]}}
40
+
41
+ dsp login --email you@acme.com --code 123456 --json
42
+ # → {"status":"authenticated","method":"otp","email":"you@acme.com"}
43
+ ```
44
+
45
+ **OTP email format** is stable and regex-parseable:
46
+
47
+ - Subject: `123456 is your display.dev sign-in code`
48
+ - Body: `Your verification code is: 123456`
49
+
50
+ Extraction pattern: `/Your verification code is: (\d{6})/`.
51
+
21
52
  ## Usage
22
53
 
23
54
  ```bash
package/dist/main.js CHANGED
@@ -137,12 +137,15 @@ function _ts_generator(thisArg, body) {
137
137
  }
138
138
  import { readFile, mkdir, writeFile, rename, chmod } from 'node:fs/promises';
139
139
  import { createInterface } from 'node:readline/promises';
140
+ import { createRequire } from 'node:module';
140
141
  import { execFileSync } from 'node:child_process';
141
142
  import { homedir, platform } from 'node:os';
142
143
  import { join, extname } from 'node:path';
143
144
  import { Command } from 'commander';
144
145
  import { ApiClient } from './api-client.js';
145
146
  import { startMcpServer } from './mcp-server.js';
147
+ var require = createRequire(import.meta.url);
148
+ var version = require('../package.json').version;
146
149
  var DEFAULT_API_URL = 'https://api.display.dev';
147
150
  var CONFIG_DIR = join(homedir(), '.displaydev');
148
151
  var CONFIG_PATH = join(CONFIG_DIR, 'config.json');
@@ -280,7 +283,7 @@ function createClient(auth) {
280
283
  clientType: 'cli'
281
284
  });
282
285
  }
283
- 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);
284
287
  function collectEmails(value, prev) {
285
288
  return prev.concat(value);
286
289
  }
@@ -510,12 +513,28 @@ function sleep(ms) {
510
513
  });
511
514
  }
512
515
  // --- login ---
513
- 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) {
516
+ program.command('login').description('Authenticate with display.dev').option('--email <email>', 'Email address').option('--code <code>', 'OTP code (verify step; pair with --email)').option('--api-key [key]', 'Authenticate with an API key').option('--json', 'Machine-readable output (structured status, no prose)').action(function(opts) {
514
517
  return _async_to_generator(function() {
515
- var _process_env_DISPLAYDEV_API_URL, apiUrl, client, key, rl, validation, rl1, email, method, check, unused, err, msg, code, result, unused1, deviceResult, unused2, device_code, verification_uri_complete, initialInterval, interval, tokenResult, unused3, _$err;
518
+ var _process_env_DISPLAYDEV_API_URL, emit, emitErr, apiUrl, client, key, rl, validation, email, rl1, method, check, unused, err, msg, code, rl2, result, unused1, deviceResult, unused2, device_code, verification_uri_complete, initialInterval, interval, tokenResult, unused3, _$err;
516
519
  return _ts_generator(this, function(_state) {
517
520
  switch(_state.label){
518
521
  case 0:
522
+ // Small helper: emit either prose (default) or a JSON line. Agents use
523
+ // --json to avoid string-matching against the prose hints.
524
+ emit = function emit(prose, json) {
525
+ if (opts.json) {
526
+ console.log(JSON.stringify(json));
527
+ } else {
528
+ console.log(prose);
529
+ }
530
+ };
531
+ emitErr = function emitErr(prose, json) {
532
+ if (opts.json) {
533
+ console.log(JSON.stringify(json));
534
+ } else {
535
+ console.error(prose);
536
+ }
537
+ };
519
538
  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;
520
539
  client = new ApiClient({
521
540
  baseUrl: apiUrl,
@@ -607,7 +626,10 @@ program.command('login').description('Authenticate with display.dev').option('--
607
626
  case 6:
608
627
  validation = _state.sent();
609
628
  if (validation === 'invalid') {
610
- console.error('Invalid API key. Check the key and try again.');
629
+ emitErr('Invalid API key. Check the key and try again.', {
630
+ status: 'error',
631
+ error: 'invalid_api_key'
632
+ });
611
633
  process.exit(1);
612
634
  }
613
635
  if (validation === 'network_error') {
@@ -622,27 +644,35 @@ program.command('login').description('Authenticate with display.dev').option('--
622
644
  ];
623
645
  case 7:
624
646
  _state.sent();
625
- console.log('Authenticated.');
647
+ emit('Authenticated.', {
648
+ status: 'authenticated',
649
+ method: 'api_key'
650
+ });
626
651
  return [
627
652
  2
628
653
  ];
629
654
  case 8:
630
655
  // --- Mode 1 & 2: Email-based login ---
631
- rl1 = createInterface({
632
- input: process.stdin,
633
- output: process.stdout
634
- });
635
656
  email = opts.email;
636
657
  if (!!email) return [
637
658
  3,
638
659
  10
639
660
  ];
661
+ if (!process.stdin.isTTY) {
662
+ console.error('Email is required. Use --email <email> for non-interactive mode.');
663
+ process.exit(1);
664
+ }
665
+ rl1 = createInterface({
666
+ input: process.stdin,
667
+ output: process.stdout
668
+ });
640
669
  return [
641
670
  4,
642
671
  rl1.question('Email: ')
643
672
  ];
644
673
  case 9:
645
674
  email = _state.sent();
675
+ rl1.close();
646
676
  _state.label = 10;
647
677
  case 10:
648
678
  email = email.trim();
@@ -676,7 +706,11 @@ program.command('login').description('Authenticate with display.dev').option('--
676
706
  case 14:
677
707
  if (!(method === 'otp')) return [
678
708
  3,
679
- 25
709
+ 27
710
+ ];
711
+ if (!!opts.code) return [
712
+ 3,
713
+ 19
680
714
  ];
681
715
  _state.label = 15;
682
716
  case 15:
@@ -698,12 +732,17 @@ program.command('login').description('Authenticate with display.dev').option('--
698
732
  ];
699
733
  case 17:
700
734
  err = _state.sent();
701
- rl1.close();
702
735
  msg = _instanceof(err, Error) ? err.message : 'Something went wrong';
703
736
  if (msg.includes('requires SSO')) {
704
- console.error('Your organization requires SSO. Run dsp login again.');
737
+ emitErr('Your organization requires SSO. Run dsp login again.', {
738
+ status: 'error',
739
+ error: 'sso_required'
740
+ });
705
741
  } else {
706
- console.error("Something went wrong. Check your connection and try again.");
742
+ emitErr('Something went wrong. Check your connection and try again.', {
743
+ status: 'error',
744
+ error: 'send_failed'
745
+ });
707
746
  }
708
747
  process.exit(1);
709
748
  return [
@@ -711,27 +750,71 @@ program.command('login').description('Authenticate with display.dev').option('--
711
750
  18
712
751
  ];
713
752
  case 18:
714
- console.log("Code sent to ".concat(email));
753
+ if (!process.stdin.isTTY) {
754
+ // Non-interactive: exit after sending. The caller (typically an
755
+ // agent) will collect the OTP from the user and re-invoke with --code.
756
+ if (opts.json) {
757
+ console.log(JSON.stringify({
758
+ status: 'otp_sent',
759
+ email: email,
760
+ next: {
761
+ command: 'dsp login',
762
+ args: [
763
+ '--email',
764
+ email,
765
+ '--code',
766
+ '<code>'
767
+ ]
768
+ }
769
+ }));
770
+ } else {
771
+ console.log("Code sent to ".concat(email));
772
+ console.log("Re-run with --code <6-digit code> to complete login.");
773
+ }
774
+ return [
775
+ 2
776
+ ];
777
+ }
778
+ // TTY path prints the same prose hint then prompts.
779
+ if (!opts.json) {
780
+ console.log("Code sent to ".concat(email));
781
+ }
782
+ _state.label = 19;
783
+ case 19:
784
+ if (!opts.code) return [
785
+ 3,
786
+ 20
787
+ ];
788
+ code = opts.code;
789
+ return [
790
+ 3,
791
+ 22
792
+ ];
793
+ case 20:
794
+ rl2 = createInterface({
795
+ input: process.stdin,
796
+ output: process.stdout
797
+ });
715
798
  return [
716
799
  4,
717
- rl1.question('Enter the 6-digit code: ')
800
+ rl2.question('Enter the 6-digit code: ')
718
801
  ];
719
- case 19:
802
+ case 21:
720
803
  code = _state.sent();
721
- rl1.close();
722
- _state.label = 20;
723
- case 20:
804
+ rl2.close();
805
+ _state.label = 22;
806
+ case 22:
724
807
  _state.trys.push([
725
- 20,
726
- 23,
808
+ 22,
809
+ 25,
727
810
  ,
728
- 24
811
+ 26
729
812
  ]);
730
813
  return [
731
814
  4,
732
815
  client.verifyOtp(email, code.trim())
733
816
  ];
734
- case 21:
817
+ case 23:
735
818
  result = _state.sent();
736
819
  return [
737
820
  4,
@@ -740,57 +823,63 @@ program.command('login').description('Authenticate with display.dev').option('--
740
823
  apiUrl: apiUrl
741
824
  })
742
825
  ];
743
- case 22:
826
+ case 24:
744
827
  _state.sent();
745
- console.log("Logged in as ".concat(email));
828
+ emit("Logged in as ".concat(email), {
829
+ status: 'authenticated',
830
+ method: 'otp',
831
+ email: email
832
+ });
746
833
  return [
747
834
  3,
748
- 24
835
+ 26
749
836
  ];
750
- case 23:
837
+ case 25:
751
838
  unused1 = _state.sent();
752
- console.error('Invalid or expired code. Try again.');
839
+ emitErr('Invalid or expired code. Try again.', {
840
+ status: 'error',
841
+ error: 'invalid_or_expired_code'
842
+ });
753
843
  process.exit(1);
754
844
  return [
755
845
  3,
756
- 24
846
+ 26
757
847
  ];
758
- case 24:
848
+ case 26:
759
849
  return [
760
850
  3,
761
- 38
851
+ 40
762
852
  ];
763
- case 25:
853
+ case 27:
764
854
  // --- Mode 2: SSO device flow ---
765
- rl1.close();
766
855
  console.log('Your organization requires SSO. Opening browser...');
767
- _state.label = 26;
768
- case 26:
856
+ _state.label = 28;
857
+ case 28:
769
858
  _state.trys.push([
770
- 26,
771
859
  28,
860
+ 30,
772
861
  ,
773
- 29
862
+ 31
774
863
  ]);
775
864
  return [
776
865
  4,
777
866
  client.requestDeviceCode('dsp-cli')
778
867
  ];
779
- case 27:
868
+ case 29:
780
869
  deviceResult = _state.sent();
781
870
  return [
782
871
  3,
783
- 29
872
+ 31
784
873
  ];
785
- case 28:
874
+ case 30:
786
875
  unused2 = _state.sent();
787
876
  console.error('Something went wrong. Check your connection and try again.');
788
877
  process.exit(1);
789
878
  return [
790
879
  3,
791
- 29
880
+ 31
792
881
  ];
793
- case 29:
882
+ case 31:
794
883
  device_code = deviceResult.device_code, verification_uri_complete = deviceResult.verification_uri_complete, initialInterval = deviceResult.interval;
795
884
  console.log();
796
885
  console.log(" ".concat(verification_uri_complete));
@@ -799,48 +888,48 @@ program.command('login').description('Authenticate with display.dev').option('--
799
888
  // Poll for token
800
889
  interval = (initialInterval || 5) * 1000;
801
890
  process.stdout.write('Waiting for authentication...');
802
- _state.label = 30;
803
- case 30:
891
+ _state.label = 32;
892
+ case 32:
804
893
  if (!true) return [
805
894
  3,
806
- 38
895
+ 40
807
896
  ];
808
897
  return [
809
898
  4,
810
899
  sleep(interval)
811
900
  ];
812
- case 31:
901
+ case 33:
813
902
  _state.sent();
814
903
  tokenResult = void 0;
815
- _state.label = 32;
816
- case 32:
904
+ _state.label = 34;
905
+ case 34:
817
906
  _state.trys.push([
818
- 32,
819
907
  34,
908
+ 36,
820
909
  ,
821
- 35
910
+ 37
822
911
  ]);
823
912
  return [
824
913
  4,
825
914
  client.pollDeviceToken(device_code, 'dsp-cli')
826
915
  ];
827
- case 33:
916
+ case 35:
828
917
  tokenResult = _state.sent();
829
918
  return [
830
919
  3,
831
- 35
920
+ 37
832
921
  ];
833
- case 34:
922
+ case 36:
834
923
  unused3 = _state.sent();
835
924
  // Transient network error — retry on next interval
836
925
  return [
837
926
  3,
838
- 30
927
+ 32
839
928
  ];
840
- case 35:
929
+ case 37:
841
930
  if (!('access_token' in tokenResult)) return [
842
931
  3,
843
- 37
932
+ 39
844
933
  ];
845
934
  console.log(' done');
846
935
  return [
@@ -850,25 +939,25 @@ program.command('login').description('Authenticate with display.dev').option('--
850
939
  apiUrl: apiUrl
851
940
  })
852
941
  ];
853
- case 36:
942
+ case 38:
854
943
  _state.sent();
855
944
  console.log("Logged in as ".concat(email));
856
945
  return [
857
946
  2
858
947
  ];
859
- case 37:
948
+ case 39:
860
949
  _$err = tokenResult;
861
950
  if (_$err.error === 'authorization_pending') {
862
951
  return [
863
952
  3,
864
- 30
953
+ 32
865
954
  ];
866
955
  }
867
956
  if (_$err.error === 'slow_down') {
868
957
  interval += 5000;
869
958
  return [
870
959
  3,
871
- 30
960
+ 32
872
961
  ];
873
962
  }
874
963
  if (_$err.error === 'access_denied') {
@@ -887,9 +976,9 @@ program.command('login').description('Authenticate with display.dev').option('--
887
976
  process.exit(1);
888
977
  return [
889
978
  3,
890
- 30
979
+ 32
891
980
  ];
892
- case 38:
981
+ case 40:
893
982
  return [
894
983
  2
895
984
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@displaydev/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "dsp": "dist/main.js"
@@ -12,6 +12,7 @@
12
12
  "scripts": {
13
13
  "build": "swc src -d dist --strip-leading-paths",
14
14
  "typecheck": "tsgo --project tsconfig.json --noEmit",
15
+ "lint": "oxlint --deny-warnings .",
15
16
  "test": "vitest run"
16
17
  },
17
18
  "dependencies": {