@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.
- package/README.md +31 -0
- package/dist/main.js +151 -62
- 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(
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
737
|
+
emitErr('Your organization requires SSO. Run dsp login again.', {
|
|
738
|
+
status: 'error',
|
|
739
|
+
error: 'sso_required'
|
|
740
|
+
});
|
|
705
741
|
} else {
|
|
706
|
-
|
|
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
|
-
|
|
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
|
-
|
|
800
|
+
rl2.question('Enter the 6-digit code: ')
|
|
718
801
|
];
|
|
719
|
-
case
|
|
802
|
+
case 21:
|
|
720
803
|
code = _state.sent();
|
|
721
|
-
|
|
722
|
-
_state.label =
|
|
723
|
-
case
|
|
804
|
+
rl2.close();
|
|
805
|
+
_state.label = 22;
|
|
806
|
+
case 22:
|
|
724
807
|
_state.trys.push([
|
|
725
|
-
|
|
726
|
-
|
|
808
|
+
22,
|
|
809
|
+
25,
|
|
727
810
|
,
|
|
728
|
-
|
|
811
|
+
26
|
|
729
812
|
]);
|
|
730
813
|
return [
|
|
731
814
|
4,
|
|
732
815
|
client.verifyOtp(email, code.trim())
|
|
733
816
|
];
|
|
734
|
-
case
|
|
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
|
|
826
|
+
case 24:
|
|
744
827
|
_state.sent();
|
|
745
|
-
|
|
828
|
+
emit("Logged in as ".concat(email), {
|
|
829
|
+
status: 'authenticated',
|
|
830
|
+
method: 'otp',
|
|
831
|
+
email: email
|
|
832
|
+
});
|
|
746
833
|
return [
|
|
747
834
|
3,
|
|
748
|
-
|
|
835
|
+
26
|
|
749
836
|
];
|
|
750
|
-
case
|
|
837
|
+
case 25:
|
|
751
838
|
unused1 = _state.sent();
|
|
752
|
-
|
|
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
|
-
|
|
846
|
+
26
|
|
757
847
|
];
|
|
758
|
-
case
|
|
848
|
+
case 26:
|
|
759
849
|
return [
|
|
760
850
|
3,
|
|
761
|
-
|
|
851
|
+
40
|
|
762
852
|
];
|
|
763
|
-
case
|
|
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 =
|
|
768
|
-
case
|
|
856
|
+
_state.label = 28;
|
|
857
|
+
case 28:
|
|
769
858
|
_state.trys.push([
|
|
770
|
-
26,
|
|
771
859
|
28,
|
|
860
|
+
30,
|
|
772
861
|
,
|
|
773
|
-
|
|
862
|
+
31
|
|
774
863
|
]);
|
|
775
864
|
return [
|
|
776
865
|
4,
|
|
777
866
|
client.requestDeviceCode('dsp-cli')
|
|
778
867
|
];
|
|
779
|
-
case
|
|
868
|
+
case 29:
|
|
780
869
|
deviceResult = _state.sent();
|
|
781
870
|
return [
|
|
782
871
|
3,
|
|
783
|
-
|
|
872
|
+
31
|
|
784
873
|
];
|
|
785
|
-
case
|
|
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
|
-
|
|
880
|
+
31
|
|
792
881
|
];
|
|
793
|
-
case
|
|
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 =
|
|
803
|
-
case
|
|
891
|
+
_state.label = 32;
|
|
892
|
+
case 32:
|
|
804
893
|
if (!true) return [
|
|
805
894
|
3,
|
|
806
|
-
|
|
895
|
+
40
|
|
807
896
|
];
|
|
808
897
|
return [
|
|
809
898
|
4,
|
|
810
899
|
sleep(interval)
|
|
811
900
|
];
|
|
812
|
-
case
|
|
901
|
+
case 33:
|
|
813
902
|
_state.sent();
|
|
814
903
|
tokenResult = void 0;
|
|
815
|
-
_state.label =
|
|
816
|
-
case
|
|
904
|
+
_state.label = 34;
|
|
905
|
+
case 34:
|
|
817
906
|
_state.trys.push([
|
|
818
|
-
32,
|
|
819
907
|
34,
|
|
908
|
+
36,
|
|
820
909
|
,
|
|
821
|
-
|
|
910
|
+
37
|
|
822
911
|
]);
|
|
823
912
|
return [
|
|
824
913
|
4,
|
|
825
914
|
client.pollDeviceToken(device_code, 'dsp-cli')
|
|
826
915
|
];
|
|
827
|
-
case
|
|
916
|
+
case 35:
|
|
828
917
|
tokenResult = _state.sent();
|
|
829
918
|
return [
|
|
830
919
|
3,
|
|
831
|
-
|
|
920
|
+
37
|
|
832
921
|
];
|
|
833
|
-
case
|
|
922
|
+
case 36:
|
|
834
923
|
unused3 = _state.sent();
|
|
835
924
|
// Transient network error — retry on next interval
|
|
836
925
|
return [
|
|
837
926
|
3,
|
|
838
|
-
|
|
927
|
+
32
|
|
839
928
|
];
|
|
840
|
-
case
|
|
929
|
+
case 37:
|
|
841
930
|
if (!('access_token' in tokenResult)) return [
|
|
842
931
|
3,
|
|
843
|
-
|
|
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
|
|
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
|
|
948
|
+
case 39:
|
|
860
949
|
_$err = tokenResult;
|
|
861
950
|
if (_$err.error === 'authorization_pending') {
|
|
862
951
|
return [
|
|
863
952
|
3,
|
|
864
|
-
|
|
953
|
+
32
|
|
865
954
|
];
|
|
866
955
|
}
|
|
867
956
|
if (_$err.error === 'slow_down') {
|
|
868
957
|
interval += 5000;
|
|
869
958
|
return [
|
|
870
959
|
3,
|
|
871
|
-
|
|
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
|
-
|
|
979
|
+
32
|
|
891
980
|
];
|
|
892
|
-
case
|
|
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.
|
|
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": {
|