@displaydev/cli 0.2.1 → 0.4.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 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
@@ -272,26 +272,44 @@ export var ApiClient = /*#__PURE__*/ function() {
272
272
  key: "find",
273
273
  value: function find(params) {
274
274
  return _async_to_generator(function() {
275
- var searchParams, qs;
275
+ var searchParams, qs, res;
276
276
  return _ts_generator(this, function(_state) {
277
- searchParams = new URLSearchParams();
278
- if (params.query) {
279
- searchParams.set('q', params.query);
280
- }
281
- if (params.published_by) {
282
- searchParams.set('published_by', params.published_by);
283
- }
284
- if (params.since) {
285
- searchParams.set('since', params.since);
286
- }
287
- if (params.limit) {
288
- searchParams.set('limit', String(params.limit));
277
+ switch(_state.label){
278
+ case 0:
279
+ searchParams = new URLSearchParams();
280
+ if (params.query) {
281
+ searchParams.set('q', params.query);
282
+ }
283
+ if (params.mine) {
284
+ searchParams.append('author', 'me');
285
+ }
286
+ if (params.published_by) {
287
+ searchParams.set('published_by', params.published_by);
288
+ }
289
+ if (params.since) {
290
+ searchParams.set('since', params.since);
291
+ }
292
+ if (params.sort) {
293
+ searchParams.set('sort', params.sort);
294
+ }
295
+ if (params.dir) {
296
+ searchParams.set('dir', params.dir);
297
+ }
298
+ if (params.limit) {
299
+ searchParams.set('limit', String(params.limit));
300
+ }
301
+ qs = searchParams.toString();
302
+ return [
303
+ 4,
304
+ this.request('GET', "/v1/artifacts".concat(qs ? "?".concat(qs) : ''))
305
+ ];
306
+ case 1:
307
+ res = _state.sent();
308
+ return [
309
+ 2,
310
+ res.data
311
+ ];
289
312
  }
290
- qs = searchParams.toString();
291
- return [
292
- 2,
293
- this.request('GET', "/v1/artifacts".concat(qs ? "?".concat(qs) : ''))
294
- ];
295
313
  });
296
314
  }).call(this);
297
315
  }
package/dist/main.js CHANGED
@@ -367,12 +367,26 @@ program.command('publish <path>').description('Publish an HTML or Markdown file'
367
367
  })();
368
368
  });
369
369
  // --- find ---
370
- program.command('find [query]').description('Search for artifacts').option('--by <email>', 'Filter by publisher email').option('--since <date>', 'Filter by date (ISO format)').action(function(query, opts) {
370
+ program.command('find [query]').description('Search for artifacts').option('--by <email>', 'Filter by publisher email').option('--mine', 'Show only artifacts you published').option('--since <date>', 'Filter by date (ISO format)').option('--sort <col>', 'Sort column: updated_at, view_count, name', 'updated_at').option('--dir <direction>', 'Sort direction: asc or desc').option('--limit <n>', 'Max rows to fetch (1-100)', function(v) {
371
+ return parseInt(v, 10);
372
+ }).action(function(query, opts) {
371
373
  return _async_to_generator(function() {
372
374
  var auth, client, results, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, a;
373
375
  return _ts_generator(this, function(_state) {
374
376
  switch(_state.label){
375
377
  case 0:
378
+ if (opts.sort && ![
379
+ 'updated_at',
380
+ 'view_count',
381
+ 'name'
382
+ ].includes(opts.sort)) {
383
+ console.error("Invalid --sort: ".concat(opts.sort, ". Use updated_at, view_count, or name."));
384
+ process.exit(1);
385
+ }
386
+ if (opts.dir && opts.dir !== 'asc' && opts.dir !== 'desc') {
387
+ console.error("Invalid --dir: ".concat(opts.dir, ". Use asc or desc."));
388
+ process.exit(1);
389
+ }
376
390
  return [
377
391
  4,
378
392
  resolveAuthOrConfig()
@@ -385,7 +399,11 @@ program.command('find [query]').description('Search for artifacts').option('--by
385
399
  client.find({
386
400
  query: query,
387
401
  published_by: opts.by,
388
- since: opts.since
402
+ mine: opts.mine,
403
+ since: opts.since,
404
+ sort: opts.sort,
405
+ dir: opts.dir,
406
+ limit: opts.limit
389
407
  })
390
408
  ];
391
409
  case 2:
@@ -513,12 +531,28 @@ function sleep(ms) {
513
531
  });
514
532
  }
515
533
  // --- login ---
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) {
534
+ 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) {
517
535
  return _async_to_generator(function() {
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;
536
+ 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;
519
537
  return _ts_generator(this, function(_state) {
520
538
  switch(_state.label){
521
539
  case 0:
540
+ // Small helper: emit either prose (default) or a JSON line. Agents use
541
+ // --json to avoid string-matching against the prose hints.
542
+ emit = function emit(prose, json) {
543
+ if (opts.json) {
544
+ console.log(JSON.stringify(json));
545
+ } else {
546
+ console.log(prose);
547
+ }
548
+ };
549
+ emitErr = function emitErr(prose, json) {
550
+ if (opts.json) {
551
+ console.log(JSON.stringify(json));
552
+ } else {
553
+ console.error(prose);
554
+ }
555
+ };
522
556
  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;
523
557
  client = new ApiClient({
524
558
  baseUrl: apiUrl,
@@ -610,7 +644,10 @@ program.command('login').description('Authenticate with display.dev').option('--
610
644
  case 6:
611
645
  validation = _state.sent();
612
646
  if (validation === 'invalid') {
613
- console.error('Invalid API key. Check the key and try again.');
647
+ emitErr('Invalid API key. Check the key and try again.', {
648
+ status: 'error',
649
+ error: 'invalid_api_key'
650
+ });
614
651
  process.exit(1);
615
652
  }
616
653
  if (validation === 'network_error') {
@@ -625,7 +662,10 @@ program.command('login').description('Authenticate with display.dev').option('--
625
662
  ];
626
663
  case 7:
627
664
  _state.sent();
628
- console.log('Authenticated.');
665
+ emit('Authenticated.', {
666
+ status: 'authenticated',
667
+ method: 'api_key'
668
+ });
629
669
  return [
630
670
  2
631
671
  ];
@@ -684,7 +724,11 @@ program.command('login').description('Authenticate with display.dev').option('--
684
724
  case 14:
685
725
  if (!(method === 'otp')) return [
686
726
  3,
687
- 25
727
+ 27
728
+ ];
729
+ if (!!opts.code) return [
730
+ 3,
731
+ 19
688
732
  ];
689
733
  _state.label = 15;
690
734
  case 15:
@@ -708,9 +752,15 @@ program.command('login').description('Authenticate with display.dev').option('--
708
752
  err = _state.sent();
709
753
  msg = _instanceof(err, Error) ? err.message : 'Something went wrong';
710
754
  if (msg.includes('requires SSO')) {
711
- console.error('Your organization requires SSO. Run dsp login again.');
755
+ emitErr('Your organization requires SSO. Run dsp login again.', {
756
+ status: 'error',
757
+ error: 'sso_required'
758
+ });
712
759
  } else {
713
- console.error('Something went wrong. Check your connection and try again.');
760
+ emitErr('Something went wrong. Check your connection and try again.', {
761
+ status: 'error',
762
+ error: 'send_failed'
763
+ });
714
764
  }
715
765
  process.exit(1);
716
766
  return [
@@ -718,11 +768,47 @@ program.command('login').description('Authenticate with display.dev').option('--
718
768
  18
719
769
  ];
720
770
  case 18:
721
- console.log("Code sent to ".concat(email));
722
771
  if (!process.stdin.isTTY) {
723
- console.error('OTP code required. Use dsp login --api-key for non-interactive auth.');
724
- process.exit(1);
772
+ // Non-interactive: exit after sending. The caller (typically an
773
+ // agent) will collect the OTP from the user and re-invoke with --code.
774
+ if (opts.json) {
775
+ console.log(JSON.stringify({
776
+ status: 'otp_sent',
777
+ email: email,
778
+ next: {
779
+ command: 'dsp login',
780
+ args: [
781
+ '--email',
782
+ email,
783
+ '--code',
784
+ '<code>'
785
+ ]
786
+ }
787
+ }));
788
+ } else {
789
+ console.log("Code sent to ".concat(email));
790
+ console.log("Re-run with --code <6-digit code> to complete login.");
791
+ }
792
+ return [
793
+ 2
794
+ ];
795
+ }
796
+ // TTY path prints the same prose hint then prompts.
797
+ if (!opts.json) {
798
+ console.log("Code sent to ".concat(email));
725
799
  }
800
+ _state.label = 19;
801
+ case 19:
802
+ if (!opts.code) return [
803
+ 3,
804
+ 20
805
+ ];
806
+ code = opts.code;
807
+ return [
808
+ 3,
809
+ 22
810
+ ];
811
+ case 20:
726
812
  rl2 = createInterface({
727
813
  input: process.stdin,
728
814
  output: process.stdout
@@ -731,22 +817,22 @@ program.command('login').description('Authenticate with display.dev').option('--
731
817
  4,
732
818
  rl2.question('Enter the 6-digit code: ')
733
819
  ];
734
- case 19:
820
+ case 21:
735
821
  code = _state.sent();
736
822
  rl2.close();
737
- _state.label = 20;
738
- case 20:
823
+ _state.label = 22;
824
+ case 22:
739
825
  _state.trys.push([
740
- 20,
741
- 23,
826
+ 22,
827
+ 25,
742
828
  ,
743
- 24
829
+ 26
744
830
  ]);
745
831
  return [
746
832
  4,
747
833
  client.verifyOtp(email, code.trim())
748
834
  ];
749
- case 21:
835
+ case 23:
750
836
  result = _state.sent();
751
837
  return [
752
838
  4,
@@ -755,56 +841,63 @@ program.command('login').description('Authenticate with display.dev').option('--
755
841
  apiUrl: apiUrl
756
842
  })
757
843
  ];
758
- case 22:
844
+ case 24:
759
845
  _state.sent();
760
- console.log("Logged in as ".concat(email));
846
+ emit("Logged in as ".concat(email), {
847
+ status: 'authenticated',
848
+ method: 'otp',
849
+ email: email
850
+ });
761
851
  return [
762
852
  3,
763
- 24
853
+ 26
764
854
  ];
765
- case 23:
855
+ case 25:
766
856
  unused1 = _state.sent();
767
- console.error('Invalid or expired code. Try again.');
857
+ emitErr('Invalid or expired code. Try again.', {
858
+ status: 'error',
859
+ error: 'invalid_or_expired_code'
860
+ });
768
861
  process.exit(1);
769
862
  return [
770
863
  3,
771
- 24
864
+ 26
772
865
  ];
773
- case 24:
866
+ case 26:
774
867
  return [
775
868
  3,
776
- 38
869
+ 40
777
870
  ];
778
- case 25:
871
+ case 27:
779
872
  // --- Mode 2: SSO device flow ---
780
873
  console.log('Your organization requires SSO. Opening browser...');
781
- _state.label = 26;
782
- case 26:
874
+ _state.label = 28;
875
+ case 28:
783
876
  _state.trys.push([
784
- 26,
785
877
  28,
878
+ 30,
786
879
  ,
787
- 29
880
+ 31
788
881
  ]);
789
882
  return [
790
883
  4,
791
884
  client.requestDeviceCode('dsp-cli')
792
885
  ];
793
- case 27:
886
+ case 29:
794
887
  deviceResult = _state.sent();
795
888
  return [
796
889
  3,
797
- 29
890
+ 31
798
891
  ];
799
- case 28:
892
+ case 30:
800
893
  unused2 = _state.sent();
801
894
  console.error('Something went wrong. Check your connection and try again.');
802
895
  process.exit(1);
803
896
  return [
804
897
  3,
805
- 29
898
+ 31
806
899
  ];
807
- case 29:
900
+ case 31:
808
901
  device_code = deviceResult.device_code, verification_uri_complete = deviceResult.verification_uri_complete, initialInterval = deviceResult.interval;
809
902
  console.log();
810
903
  console.log(" ".concat(verification_uri_complete));
@@ -813,48 +906,48 @@ program.command('login').description('Authenticate with display.dev').option('--
813
906
  // Poll for token
814
907
  interval = (initialInterval || 5) * 1000;
815
908
  process.stdout.write('Waiting for authentication...');
816
- _state.label = 30;
817
- case 30:
909
+ _state.label = 32;
910
+ case 32:
818
911
  if (!true) return [
819
912
  3,
820
- 38
913
+ 40
821
914
  ];
822
915
  return [
823
916
  4,
824
917
  sleep(interval)
825
918
  ];
826
- case 31:
919
+ case 33:
827
920
  _state.sent();
828
921
  tokenResult = void 0;
829
- _state.label = 32;
830
- case 32:
922
+ _state.label = 34;
923
+ case 34:
831
924
  _state.trys.push([
832
- 32,
833
925
  34,
926
+ 36,
834
927
  ,
835
- 35
928
+ 37
836
929
  ]);
837
930
  return [
838
931
  4,
839
932
  client.pollDeviceToken(device_code, 'dsp-cli')
840
933
  ];
841
- case 33:
934
+ case 35:
842
935
  tokenResult = _state.sent();
843
936
  return [
844
937
  3,
845
- 35
938
+ 37
846
939
  ];
847
- case 34:
940
+ case 36:
848
941
  unused3 = _state.sent();
849
942
  // Transient network error — retry on next interval
850
943
  return [
851
944
  3,
852
- 30
945
+ 32
853
946
  ];
854
- case 35:
947
+ case 37:
855
948
  if (!('access_token' in tokenResult)) return [
856
949
  3,
857
- 37
950
+ 39
858
951
  ];
859
952
  console.log(' done');
860
953
  return [
@@ -864,25 +957,25 @@ program.command('login').description('Authenticate with display.dev').option('--
864
957
  apiUrl: apiUrl
865
958
  })
866
959
  ];
867
- case 36:
960
+ case 38:
868
961
  _state.sent();
869
962
  console.log("Logged in as ".concat(email));
870
963
  return [
871
964
  2
872
965
  ];
873
- case 37:
966
+ case 39:
874
967
  _$err = tokenResult;
875
968
  if (_$err.error === 'authorization_pending') {
876
969
  return [
877
970
  3,
878
- 30
971
+ 32
879
972
  ];
880
973
  }
881
974
  if (_$err.error === 'slow_down') {
882
975
  interval += 5000;
883
976
  return [
884
977
  3,
885
- 30
978
+ 32
886
979
  ];
887
980
  }
888
981
  if (_$err.error === 'access_denied') {
@@ -901,9 +994,9 @@ program.command('login').description('Authenticate with display.dev').option('--
901
994
  process.exit(1);
902
995
  return [
903
996
  3,
904
- 30
997
+ 32
905
998
  ];
906
- case 38:
999
+ case 40:
907
1000
  return [
908
1001
  2
909
1002
  ];
@@ -309,8 +309,18 @@ function registerTools(server, api) {
309
309
  server.tool('find', 'Search for artifacts in the organization', {
310
310
  query: z.string().optional().describe('Search by artifact name'),
311
311
  published_by: z.string().optional().describe('Filter by publisher email'),
312
+ mine: z.boolean().optional().describe('Limit results to your own artifacts'),
312
313
  since: z.string().optional().describe('Filter by updated since ISO date'),
313
- limit: z.number().optional().describe('Max results to return')
314
+ sort: z.enum([
315
+ 'updated_at',
316
+ 'view_count',
317
+ 'name'
318
+ ]).optional().describe('Sort column'),
319
+ dir: z.enum([
320
+ 'asc',
321
+ 'desc'
322
+ ]).optional().describe('Sort direction'),
323
+ limit: z.number().optional().describe('Max results to return (1-100)')
314
324
  }, function(args) {
315
325
  return _async_to_generator(function() {
316
326
  var results;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@displaydev/cli",
3
- "version": "0.2.1",
3
+ "version": "0.4.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": {