@qulib/core 0.4.0 → 0.4.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.
@@ -367,67 +367,6 @@ export declare const HarnessConfigSchema: z.ZodObject<{
367
367
  }>;
368
368
  export type HarnessConfig = z.infer<typeof HarnessConfigSchema>;
369
369
  export declare function resolveMaxOutputTokensPerLlmCall(config: HarnessConfig): number;
370
- export declare const DetectedAuthSchema: z.ZodObject<{
371
- hasAuth: z.ZodBoolean;
372
- type: z.ZodEnum<["none", "form-login", "oauth", "magic-link", "unknown"]>;
373
- provider: z.ZodNullable<z.ZodString>;
374
- loginUrl: z.ZodNullable<z.ZodString>;
375
- observedSelectors: z.ZodNullable<z.ZodObject<{
376
- usernameSelector: z.ZodNullable<z.ZodString>;
377
- passwordSelector: z.ZodNullable<z.ZodString>;
378
- submitSelector: z.ZodNullable<z.ZodString>;
379
- }, "strip", z.ZodTypeAny, {
380
- usernameSelector: string | null;
381
- passwordSelector: string | null;
382
- submitSelector: string | null;
383
- }, {
384
- usernameSelector: string | null;
385
- passwordSelector: string | null;
386
- submitSelector: string | null;
387
- }>>;
388
- oauthButtons: z.ZodArray<z.ZodObject<{
389
- provider: z.ZodString;
390
- text: z.ZodString;
391
- }, "strip", z.ZodTypeAny, {
392
- provider: string;
393
- text: string;
394
- }, {
395
- provider: string;
396
- text: string;
397
- }>, "many">;
398
- recommendation: z.ZodString;
399
- }, "strip", z.ZodTypeAny, {
400
- type: "unknown" | "form-login" | "none" | "oauth" | "magic-link";
401
- loginUrl: string | null;
402
- hasAuth: boolean;
403
- provider: string | null;
404
- observedSelectors: {
405
- usernameSelector: string | null;
406
- passwordSelector: string | null;
407
- submitSelector: string | null;
408
- } | null;
409
- oauthButtons: {
410
- provider: string;
411
- text: string;
412
- }[];
413
- recommendation: string;
414
- }, {
415
- type: "unknown" | "form-login" | "none" | "oauth" | "magic-link";
416
- loginUrl: string | null;
417
- hasAuth: boolean;
418
- provider: string | null;
419
- observedSelectors: {
420
- usernameSelector: string | null;
421
- passwordSelector: string | null;
422
- submitSelector: string | null;
423
- } | null;
424
- oauthButtons: {
425
- provider: string;
426
- text: string;
427
- }[];
428
- recommendation: string;
429
- }>;
430
- export type DetectedAuth = z.infer<typeof DetectedAuthSchema>;
431
370
  export declare const AuthPathRequirementsSchema: z.ZodDiscriminatedUnion<"method", [z.ZodObject<{
432
371
  method: z.ZodLiteral<"storage-state">;
433
372
  instruction: z.ZodString;
@@ -543,10 +482,10 @@ export declare const AuthPathSchema: z.ZodObject<{
543
482
  instruction: string;
544
483
  }>]>;
545
484
  }, "strip", z.ZodTypeAny, {
546
- type: "unknown" | "form-login" | "oauth" | "magic-link" | "oauth-unknown" | "form-multi";
547
- provider: string | null;
485
+ type: "unknown" | "form-login" | "oauth" | "oauth-unknown" | "form-multi" | "magic-link";
548
486
  label: string;
549
487
  id: string;
488
+ provider: string | null;
550
489
  source: "built-in" | "user-local" | "heuristic";
551
490
  automatable: boolean;
552
491
  confidence: "high" | "medium" | "low";
@@ -566,10 +505,10 @@ export declare const AuthPathSchema: z.ZodObject<{
566
505
  instruction: string;
567
506
  };
568
507
  }, {
569
- type: "unknown" | "form-login" | "oauth" | "magic-link" | "oauth-unknown" | "form-multi";
570
- provider: string | null;
508
+ type: "unknown" | "form-login" | "oauth" | "oauth-unknown" | "form-multi" | "magic-link";
571
509
  label: string;
572
510
  id: string;
511
+ provider: string | null;
573
512
  source: "built-in" | "user-local" | "heuristic";
574
513
  automatable: boolean;
575
514
  confidence: "high" | "medium" | "low";
@@ -589,6 +528,223 @@ export declare const AuthPathSchema: z.ZodObject<{
589
528
  instruction: string;
590
529
  };
591
530
  }>;
531
+ export declare const DetectedAuthSchema: z.ZodObject<{
532
+ hasAuth: z.ZodBoolean;
533
+ type: z.ZodEnum<["none", "form-login", "oauth", "magic-link", "unknown"]>;
534
+ provider: z.ZodNullable<z.ZodString>;
535
+ loginUrl: z.ZodNullable<z.ZodString>;
536
+ observedSelectors: z.ZodNullable<z.ZodObject<{
537
+ usernameSelector: z.ZodNullable<z.ZodString>;
538
+ passwordSelector: z.ZodNullable<z.ZodString>;
539
+ submitSelector: z.ZodNullable<z.ZodString>;
540
+ }, "strip", z.ZodTypeAny, {
541
+ usernameSelector: string | null;
542
+ passwordSelector: string | null;
543
+ submitSelector: string | null;
544
+ }, {
545
+ usernameSelector: string | null;
546
+ passwordSelector: string | null;
547
+ submitSelector: string | null;
548
+ }>>;
549
+ oauthButtons: z.ZodArray<z.ZodObject<{
550
+ provider: z.ZodString;
551
+ text: z.ZodString;
552
+ }, "strip", z.ZodTypeAny, {
553
+ text: string;
554
+ provider: string;
555
+ }, {
556
+ text: string;
557
+ provider: string;
558
+ }>, "many">;
559
+ authOptions: z.ZodOptional<z.ZodArray<z.ZodObject<{
560
+ id: z.ZodString;
561
+ label: z.ZodString;
562
+ type: z.ZodEnum<["oauth", "oauth-unknown", "form-login", "form-multi", "magic-link", "unknown"]>;
563
+ provider: z.ZodNullable<z.ZodString>;
564
+ source: z.ZodEnum<["built-in", "user-local", "heuristic"]>;
565
+ automatable: z.ZodBoolean;
566
+ confidence: z.ZodEnum<["high", "medium", "low"]>;
567
+ requirements: z.ZodDiscriminatedUnion<"method", [z.ZodObject<{
568
+ method: z.ZodLiteral<"storage-state">;
569
+ instruction: z.ZodString;
570
+ }, "strip", z.ZodTypeAny, {
571
+ method: "storage-state";
572
+ instruction: string;
573
+ }, {
574
+ method: "storage-state";
575
+ instruction: string;
576
+ }>, z.ZodObject<{
577
+ method: z.ZodLiteral<"credentials">;
578
+ fields: z.ZodArray<z.ZodObject<{
579
+ name: z.ZodString;
580
+ label: z.ZodString;
581
+ type: z.ZodEnum<["text", "password", "email", "select", "checkbox"]>;
582
+ observedOptions: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
583
+ }, "strip", z.ZodTypeAny, {
584
+ type: "password" | "text" | "email" | "select" | "checkbox";
585
+ name: string;
586
+ label: string;
587
+ observedOptions: string[];
588
+ }, {
589
+ type: "password" | "text" | "email" | "select" | "checkbox";
590
+ name: string;
591
+ label: string;
592
+ observedOptions?: string[] | undefined;
593
+ }>, "many">;
594
+ }, "strip", z.ZodTypeAny, {
595
+ method: "credentials";
596
+ fields: {
597
+ type: "password" | "text" | "email" | "select" | "checkbox";
598
+ name: string;
599
+ label: string;
600
+ observedOptions: string[];
601
+ }[];
602
+ }, {
603
+ method: "credentials";
604
+ fields: {
605
+ type: "password" | "text" | "email" | "select" | "checkbox";
606
+ name: string;
607
+ label: string;
608
+ observedOptions?: string[] | undefined;
609
+ }[];
610
+ }>, z.ZodObject<{
611
+ method: z.ZodLiteral<"unknown">;
612
+ instruction: z.ZodString;
613
+ }, "strip", z.ZodTypeAny, {
614
+ method: "unknown";
615
+ instruction: string;
616
+ }, {
617
+ method: "unknown";
618
+ instruction: string;
619
+ }>]>;
620
+ }, "strip", z.ZodTypeAny, {
621
+ type: "unknown" | "form-login" | "oauth" | "oauth-unknown" | "form-multi" | "magic-link";
622
+ label: string;
623
+ id: string;
624
+ provider: string | null;
625
+ source: "built-in" | "user-local" | "heuristic";
626
+ automatable: boolean;
627
+ confidence: "high" | "medium" | "low";
628
+ requirements: {
629
+ method: "storage-state";
630
+ instruction: string;
631
+ } | {
632
+ method: "credentials";
633
+ fields: {
634
+ type: "password" | "text" | "email" | "select" | "checkbox";
635
+ name: string;
636
+ label: string;
637
+ observedOptions: string[];
638
+ }[];
639
+ } | {
640
+ method: "unknown";
641
+ instruction: string;
642
+ };
643
+ }, {
644
+ type: "unknown" | "form-login" | "oauth" | "oauth-unknown" | "form-multi" | "magic-link";
645
+ label: string;
646
+ id: string;
647
+ provider: string | null;
648
+ source: "built-in" | "user-local" | "heuristic";
649
+ automatable: boolean;
650
+ confidence: "high" | "medium" | "low";
651
+ requirements: {
652
+ method: "storage-state";
653
+ instruction: string;
654
+ } | {
655
+ method: "credentials";
656
+ fields: {
657
+ type: "password" | "text" | "email" | "select" | "checkbox";
658
+ name: string;
659
+ label: string;
660
+ observedOptions?: string[] | undefined;
661
+ }[];
662
+ } | {
663
+ method: "unknown";
664
+ instruction: string;
665
+ };
666
+ }>, "many">>;
667
+ recommendation: z.ZodString;
668
+ }, "strip", z.ZodTypeAny, {
669
+ type: "unknown" | "form-login" | "oauth" | "magic-link" | "none";
670
+ loginUrl: string | null;
671
+ provider: string | null;
672
+ hasAuth: boolean;
673
+ observedSelectors: {
674
+ usernameSelector: string | null;
675
+ passwordSelector: string | null;
676
+ submitSelector: string | null;
677
+ } | null;
678
+ oauthButtons: {
679
+ text: string;
680
+ provider: string;
681
+ }[];
682
+ recommendation: string;
683
+ authOptions?: {
684
+ type: "unknown" | "form-login" | "oauth" | "oauth-unknown" | "form-multi" | "magic-link";
685
+ label: string;
686
+ id: string;
687
+ provider: string | null;
688
+ source: "built-in" | "user-local" | "heuristic";
689
+ automatable: boolean;
690
+ confidence: "high" | "medium" | "low";
691
+ requirements: {
692
+ method: "storage-state";
693
+ instruction: string;
694
+ } | {
695
+ method: "credentials";
696
+ fields: {
697
+ type: "password" | "text" | "email" | "select" | "checkbox";
698
+ name: string;
699
+ label: string;
700
+ observedOptions: string[];
701
+ }[];
702
+ } | {
703
+ method: "unknown";
704
+ instruction: string;
705
+ };
706
+ }[] | undefined;
707
+ }, {
708
+ type: "unknown" | "form-login" | "oauth" | "magic-link" | "none";
709
+ loginUrl: string | null;
710
+ provider: string | null;
711
+ hasAuth: boolean;
712
+ observedSelectors: {
713
+ usernameSelector: string | null;
714
+ passwordSelector: string | null;
715
+ submitSelector: string | null;
716
+ } | null;
717
+ oauthButtons: {
718
+ text: string;
719
+ provider: string;
720
+ }[];
721
+ recommendation: string;
722
+ authOptions?: {
723
+ type: "unknown" | "form-login" | "oauth" | "oauth-unknown" | "form-multi" | "magic-link";
724
+ label: string;
725
+ id: string;
726
+ provider: string | null;
727
+ source: "built-in" | "user-local" | "heuristic";
728
+ automatable: boolean;
729
+ confidence: "high" | "medium" | "low";
730
+ requirements: {
731
+ method: "storage-state";
732
+ instruction: string;
733
+ } | {
734
+ method: "credentials";
735
+ fields: {
736
+ type: "password" | "text" | "email" | "select" | "checkbox";
737
+ name: string;
738
+ label: string;
739
+ observedOptions?: string[] | undefined;
740
+ }[];
741
+ } | {
742
+ method: "unknown";
743
+ instruction: string;
744
+ };
745
+ }[] | undefined;
746
+ }>;
747
+ export type DetectedAuth = z.infer<typeof DetectedAuthSchema>;
592
748
  export declare const AuthExplorationSchema: z.ZodObject<{
593
749
  url: z.ZodString;
594
750
  authRequired: z.ZodBoolean;
@@ -655,10 +811,10 @@ export declare const AuthExplorationSchema: z.ZodObject<{
655
811
  instruction: string;
656
812
  }>]>;
657
813
  }, "strip", z.ZodTypeAny, {
658
- type: "unknown" | "form-login" | "oauth" | "magic-link" | "oauth-unknown" | "form-multi";
659
- provider: string | null;
814
+ type: "unknown" | "form-login" | "oauth" | "oauth-unknown" | "form-multi" | "magic-link";
660
815
  label: string;
661
816
  id: string;
817
+ provider: string | null;
662
818
  source: "built-in" | "user-local" | "heuristic";
663
819
  automatable: boolean;
664
820
  confidence: "high" | "medium" | "low";
@@ -678,10 +834,10 @@ export declare const AuthExplorationSchema: z.ZodObject<{
678
834
  instruction: string;
679
835
  };
680
836
  }, {
681
- type: "unknown" | "form-login" | "oauth" | "magic-link" | "oauth-unknown" | "form-multi";
682
- provider: string | null;
837
+ type: "unknown" | "form-login" | "oauth" | "oauth-unknown" | "form-multi" | "magic-link";
683
838
  label: string;
684
839
  id: string;
840
+ provider: string | null;
685
841
  source: "built-in" | "user-local" | "heuristic";
686
842
  automatable: boolean;
687
843
  confidence: "high" | "medium" | "low";
@@ -718,10 +874,10 @@ export declare const AuthExplorationSchema: z.ZodObject<{
718
874
  authRequired: boolean;
719
875
  authScope: "none" | "site-wide" | "section-only" | "optional";
720
876
  authPaths: {
721
- type: "unknown" | "form-login" | "oauth" | "magic-link" | "oauth-unknown" | "form-multi";
722
- provider: string | null;
877
+ type: "unknown" | "form-login" | "oauth" | "oauth-unknown" | "form-multi" | "magic-link";
723
878
  label: string;
724
879
  id: string;
880
+ provider: string | null;
725
881
  source: "built-in" | "user-local" | "heuristic";
726
882
  automatable: boolean;
727
883
  confidence: "high" | "medium" | "low";
@@ -752,10 +908,10 @@ export declare const AuthExplorationSchema: z.ZodObject<{
752
908
  authRequired: boolean;
753
909
  authScope: "none" | "site-wide" | "section-only" | "optional";
754
910
  authPaths: {
755
- type: "unknown" | "form-login" | "oauth" | "magic-link" | "oauth-unknown" | "form-multi";
756
- provider: string | null;
911
+ type: "unknown" | "form-login" | "oauth" | "oauth-unknown" | "form-multi" | "magic-link";
757
912
  label: string;
758
913
  id: string;
914
+ provider: string | null;
759
915
  source: "built-in" | "user-local" | "heuristic";
760
916
  automatable: boolean;
761
917
  confidence: "high" | "medium" | "low";
@@ -1 +1 @@
1
- {"version":3,"file":"config.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/config.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,SAAS,CAAC;AACpD,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,aAAa,GAAG,mBAAmB,GAAG,KAAK,GAAG,eAAe,CAAC;AAEvG,QAAA,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBvB,CAAC;AAEH,QAAA,MAAM,sBAAsB;;;;;;;;;EAG1B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAA8E,CAAC;AAE5G,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACtE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAC5E,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0C9B,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAE9E;AAED,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmB7B,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAcrC,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASzB,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAehC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC9E,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AACtD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
1
+ {"version":3,"file":"config.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/config.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,SAAS,CAAC;AACpD,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,aAAa,GAAG,mBAAmB,GAAG,KAAK,GAAG,eAAe,CAAC;AAEvG,QAAA,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBvB,CAAC;AAEH,QAAA,MAAM,sBAAsB;;;;;;;;;EAG1B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAA8E,CAAC;AAE5G,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACtE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAC5E,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0C9B,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAE9E;AAED,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAcrC,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASzB,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoB7B,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAehC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC9E,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AACtD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
@@ -65,24 +65,6 @@ export const HarnessConfigSchema = z.object({
65
65
  export function resolveMaxOutputTokensPerLlmCall(config) {
66
66
  return config.llmMaxOutputTokensPerCall ?? config.llmTokenBudget;
67
67
  }
68
- export const DetectedAuthSchema = z.object({
69
- hasAuth: z.boolean(),
70
- type: z.enum(['none', 'form-login', 'oauth', 'magic-link', 'unknown']),
71
- provider: z.string().nullable(),
72
- loginUrl: z.string().nullable(),
73
- observedSelectors: z
74
- .object({
75
- usernameSelector: z.string().nullable(),
76
- passwordSelector: z.string().nullable(),
77
- submitSelector: z.string().nullable(),
78
- })
79
- .nullable(),
80
- oauthButtons: z.array(z.object({
81
- provider: z.string(),
82
- text: z.string(),
83
- })),
84
- recommendation: z.string(),
85
- });
86
68
  export const AuthPathRequirementsSchema = z.discriminatedUnion('method', [
87
69
  z.object({ method: z.literal('storage-state'), instruction: z.string() }),
88
70
  z.object({
@@ -106,6 +88,25 @@ export const AuthPathSchema = z.object({
106
88
  confidence: z.enum(['high', 'medium', 'low']),
107
89
  requirements: AuthPathRequirementsSchema,
108
90
  });
91
+ export const DetectedAuthSchema = z.object({
92
+ hasAuth: z.boolean(),
93
+ type: z.enum(['none', 'form-login', 'oauth', 'magic-link', 'unknown']),
94
+ provider: z.string().nullable(),
95
+ loginUrl: z.string().nullable(),
96
+ observedSelectors: z
97
+ .object({
98
+ usernameSelector: z.string().nullable(),
99
+ passwordSelector: z.string().nullable(),
100
+ submitSelector: z.string().nullable(),
101
+ })
102
+ .nullable(),
103
+ oauthButtons: z.array(z.object({
104
+ provider: z.string(),
105
+ text: z.string(),
106
+ })),
107
+ authOptions: z.array(AuthPathSchema).optional(),
108
+ recommendation: z.string(),
109
+ });
109
110
  export const AuthExplorationSchema = z.object({
110
111
  url: z.string(),
111
112
  authRequired: z.boolean(),
@@ -1 +1 @@
1
- {"version":3,"file":"auth-detector.d.ts","sourceRoot":"","sources":["../../src/tools/auth-detector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAgEtE,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,SAAS,SAAQ,EACjB,QAAQ,CAAC,EAAE,mBAAmB,GAC7B,OAAO,CAAC,YAAY,CAAC,CA8HvB"}
1
+ {"version":3,"file":"auth-detector.d.ts","sourceRoot":"","sources":["../../src/tools/auth-detector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAY,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAsPtE,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,SAAS,SAAQ,EACjB,QAAQ,CAAC,EAAE,mBAAmB,GAC7B,OAAO,CAAC,YAAY,CAAC,CAkJvB"}
@@ -1,4 +1,5 @@
1
1
  import { launchBrowser } from './browser.js';
2
+ import { BUILT_IN_OAUTH_PROVIDERS } from './oauth-providers.js';
2
3
  async function waitNetworkIdleBestEffort(page) {
3
4
  try {
4
5
  await page.waitForLoadState('networkidle', { timeout: 5000 });
@@ -7,27 +8,20 @@ async function waitNetworkIdleBestEffort(page) {
7
8
  // best-effort — analytics or polling can prevent networkidle
8
9
  }
9
10
  }
10
- const OAUTH_PROVIDERS = [
11
- { provider: 'github', patterns: [/github/i, /sign in with github/i] },
12
- {
13
- provider: 'google',
14
- patterns: [/google/i, /sign in with google/i, /accounts\.google\.com/i],
15
- },
16
- {
17
- provider: 'microsoft',
18
- patterns: [/microsoft/i, /sign in with microsoft/i, /login\.microsoftonline\.com/i],
19
- },
20
- { provider: 'apple', patterns: [/apple/i, /sign in with apple/i] },
21
- { provider: 'auth0', patterns: [/auth0/i] },
22
- { provider: 'okta', patterns: [/okta/i] },
23
- ];
11
+ const PROVIDER_LABELS = new Set(BUILT_IN_OAUTH_PROVIDERS.map((p) => p.label.toLowerCase()));
24
12
  function textLooksLikeOAuthIdpButton(text) {
25
13
  const t = text.trim();
26
14
  if (t.length === 0 || t.length > 120) {
27
15
  return false;
28
16
  }
29
- return (/\b(sign in with|log in with|continue with|sign up with)\b/i.test(t) ||
30
- /^(github|google|microsoft|apple)$/i.test(t));
17
+ if (/\b(sign in with|log in with|continue with|sign up with)\b/i.test(t)) {
18
+ return true;
19
+ }
20
+ // Accept single-word / short labels that exactly match a known provider name
21
+ if (PROVIDER_LABELS.has(t.toLowerCase())) {
22
+ return true;
23
+ }
24
+ return false;
31
25
  }
32
26
  const MAGIC_LINK_PATTERNS = [
33
27
  /email me a (sign[- ]?in )?link/i,
@@ -53,6 +47,177 @@ async function firstTextInputNameForLogin(page) {
53
47
  function debugAuth() {
54
48
  return process.env.QULIB_DEBUG === '1';
55
49
  }
50
+ function slugify(label) {
51
+ const s = label
52
+ .toLowerCase()
53
+ .replace(/\s+/g, '-')
54
+ .replace(/[^a-z0-9-]+/g, '')
55
+ .replace(/^-+|-+$/g, '');
56
+ return s.length > 0 ? s : 'custom';
57
+ }
58
+ function escapeRegExp(s) {
59
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
60
+ }
61
+ async function resolveVisibleFieldLabel(page, el) {
62
+ const id = await el.getAttribute('id');
63
+ if (id) {
64
+ const lt = await page.locator(`label[for="${id.replace(/"/g, '\\"')}"]`).first().textContent().catch(() => null);
65
+ const fromLabel = (lt ?? '').trim();
66
+ if (fromLabel)
67
+ return fromLabel;
68
+ }
69
+ const placeholder = (await el.getAttribute('placeholder'))?.trim();
70
+ if (placeholder)
71
+ return placeholder;
72
+ const aria = (await el.getAttribute('aria-label'))?.trim();
73
+ if (aria)
74
+ return aria;
75
+ const name = (await el.getAttribute('name'))?.trim();
76
+ if (name)
77
+ return name;
78
+ const typ = (await el.getAttribute('type'))?.trim();
79
+ return typ && typ !== 'select' ? typ : 'text';
80
+ }
81
+ async function deriveCredentialFieldName(el) {
82
+ const name = (await el.getAttribute('name'))?.trim();
83
+ if (name)
84
+ return name;
85
+ const placeholder = (await el.getAttribute('placeholder'))?.trim();
86
+ if (placeholder)
87
+ return slugify(placeholder);
88
+ const aria = (await el.getAttribute('aria-label'))?.trim();
89
+ if (aria)
90
+ return slugify(aria);
91
+ const id = (await el.getAttribute('id'))?.trim();
92
+ if (id)
93
+ return slugify(id);
94
+ return 'field';
95
+ }
96
+ async function buildCredentialFieldsFromVisibleForm(page) {
97
+ const fields = [];
98
+ const seen = new Set();
99
+ const loc = page.locator('input[type="text"]:visible, input[type="email"]:visible, input[type="password"]:visible, select:visible');
100
+ const count = await loc.count();
101
+ for (let i = 0; i < count; i++) {
102
+ const el = loc.nth(i);
103
+ const tag = await el.evaluate((node) => node.tagName.toLowerCase()).catch(() => '');
104
+ if (tag === 'select') {
105
+ const name = await deriveCredentialFieldName(el);
106
+ const label = await resolveVisibleFieldLabel(page, el);
107
+ const opts = await el.locator('option').allInnerTexts();
108
+ const observedOptions = opts.map((o) => o.trim()).filter((x) => x.length > 0).slice(0, 20);
109
+ const dedupeKey = `select|${name}|${label}`;
110
+ if (seen.has(dedupeKey))
111
+ continue;
112
+ seen.add(dedupeKey);
113
+ fields.push({ name, label, type: 'select', observedOptions });
114
+ continue;
115
+ }
116
+ const rawType = ((await el.getAttribute('type')) ?? 'text').toLowerCase();
117
+ if (rawType === 'hidden')
118
+ continue;
119
+ const fieldType = rawType === 'email' ? 'email' : rawType === 'password' ? 'password' : 'text';
120
+ const name = await deriveCredentialFieldName(el);
121
+ const label = await resolveVisibleFieldLabel(page, el);
122
+ const placeholder = (await el.getAttribute('placeholder'))?.trim() ?? '';
123
+ const dedupeKey = `${name}|${placeholder}`;
124
+ if (seen.has(dedupeKey))
125
+ continue;
126
+ seen.add(dedupeKey);
127
+ fields.push({ name, label, type: fieldType, observedOptions: [] });
128
+ }
129
+ return fields;
130
+ }
131
+ function authPathsFromOauthButtons(oauthButtons, loginUrl) {
132
+ return oauthButtons.map((b) => {
133
+ const isUnknown = b.provider === 'unknown';
134
+ const id = isUnknown ? slugify(b.text) : b.provider;
135
+ return {
136
+ id,
137
+ label: b.text,
138
+ type: isUnknown ? 'oauth-unknown' : 'oauth',
139
+ provider: isUnknown ? slugify(b.text) : b.provider,
140
+ source: isUnknown ? 'heuristic' : 'built-in',
141
+ automatable: false,
142
+ confidence: isUnknown ? 'low' : 'high',
143
+ requirements: {
144
+ method: 'storage-state',
145
+ instruction: `Run qulib auth init --base-url ${loginUrl}`,
146
+ },
147
+ };
148
+ });
149
+ }
150
+ async function probeClickToRevealForms(page, loginUrl, alreadyMatchedTexts, timeoutMs, progress) {
151
+ const out = [];
152
+ const buttons = page.locator('button');
153
+ const n = await buttons.count();
154
+ const seenLabels = new Set();
155
+ const SUBMIT_RE = /^(sign in|log in|submit|continue|next|cancel|close)$/i;
156
+ let candidateAttempts = 0;
157
+ for (let i = 0; i < n && candidateAttempts < 4; i++) {
158
+ const label = ((await buttons.nth(i).textContent()) ?? '').trim();
159
+ if (!label || label.length > 80)
160
+ continue;
161
+ if (alreadyMatchedTexts.has(label))
162
+ continue;
163
+ if (SUBMIT_RE.test(label))
164
+ continue;
165
+ if (seenLabels.has(label))
166
+ continue;
167
+ seenLabels.add(label);
168
+ candidateAttempts += 1;
169
+ if (debugAuth()) {
170
+ progress?.debug(`detect_auth click-reveal try label="${label.slice(0, 80)}"`);
171
+ }
172
+ let clicked = false;
173
+ try {
174
+ await page.getByRole('button', { name: label, exact: true }).first().click({ timeout: 2000 });
175
+ clicked = true;
176
+ }
177
+ catch {
178
+ try {
179
+ await page
180
+ .locator('button')
181
+ .filter({ hasText: new RegExp(`^\\s*${escapeRegExp(label)}\\s*$`, 'i') })
182
+ .first()
183
+ .click({ timeout: 2000 });
184
+ clicked = true;
185
+ }
186
+ catch {
187
+ /* skip */
188
+ }
189
+ }
190
+ if (!clicked) {
191
+ continue;
192
+ }
193
+ try {
194
+ await page.locator('input[type="password"]').first().waitFor({ state: 'visible', timeout: 2000 });
195
+ }
196
+ catch {
197
+ await page.goto(loginUrl, { timeout: timeoutMs, waitUntil: 'domcontentloaded' });
198
+ await waitNetworkIdleBestEffort(page);
199
+ continue;
200
+ }
201
+ const fields = await buildCredentialFieldsFromVisibleForm(page);
202
+ const slug = slugify(label);
203
+ out.push({
204
+ id: slug,
205
+ label,
206
+ type: 'form-login',
207
+ provider: slug,
208
+ source: 'heuristic',
209
+ automatable: true,
210
+ confidence: 'medium',
211
+ requirements: {
212
+ method: 'credentials',
213
+ fields,
214
+ },
215
+ });
216
+ await page.goto(loginUrl, { timeout: timeoutMs, waitUntil: 'domcontentloaded' });
217
+ await waitNetworkIdleBestEffort(page);
218
+ }
219
+ return out;
220
+ }
56
221
  export async function detectAuth(url, timeoutMs = 15000, progress) {
57
222
  const browser = await launchBrowser();
58
223
  try {
@@ -96,18 +261,27 @@ export async function detectAuth(url, timeoutMs = 15000, progress) {
96
261
  }
97
262
  continue;
98
263
  }
99
- for (const { provider, patterns } of OAUTH_PROVIDERS) {
264
+ let matchedAny = false;
265
+ for (const { id, patterns } of BUILT_IN_OAUTH_PROVIDERS) {
100
266
  const matched = patterns.some((p) => p.test(trimmed));
101
267
  if (debugAuth()) {
102
- progress?.debug(`detect_auth oauth pattern try provider=${provider} matched=${matched}`);
268
+ progress?.debug(`detect_auth oauth pattern try provider=${id} matched=${matched}`);
103
269
  }
104
270
  if (matched) {
105
- if (!oauthButtons.find((b) => b.provider === provider)) {
106
- oauthButtons.push({ provider, text: trimmed.slice(0, 100) });
271
+ if (!oauthButtons.find((b) => b.provider === id)) {
272
+ oauthButtons.push({ provider: id, text: trimmed.slice(0, 100) });
107
273
  }
274
+ matchedAny = true;
108
275
  }
109
276
  }
277
+ // Capture unrecognized SSO-like buttons so they appear in the result
278
+ if (!matchedAny && !oauthButtons.find((b) => b.text === trimmed.slice(0, 100))) {
279
+ oauthButtons.push({ provider: 'unknown', text: trimmed.slice(0, 100) });
280
+ }
110
281
  }
282
+ // Only skip buttons already tied to a built-in IdP — leave `unknown` labels probe-able for click-to-reveal forms.
283
+ const skipProbeLabels = new Set(oauthButtons.filter((b) => b.provider !== 'unknown').map((b) => b.text.trim()));
284
+ const clickRevealForms = await probeClickToRevealForms(page, loginUrl, skipProbeLabels, timeoutMs, progress);
111
285
  const pageText = await page.locator('body').innerText().catch(() => '');
112
286
  const hasMagicLink = MAGIC_LINK_PATTERNS.some((p) => p.test(pageText));
113
287
  let type = 'none';
@@ -117,7 +291,7 @@ export async function detectAuth(url, timeoutMs = 15000, progress) {
117
291
  if (oauthButtons.length > 0) {
118
292
  type = 'oauth';
119
293
  provider = oauthButtons[0].provider;
120
- recommendation = `OAuth detected (${oauthButtons.map((b) => b.provider).join(', ')}). OAuth cannot be automated. Run "qulib auth init --base-url ${url}" to log in manually once and save a reusable storage state file.`;
294
+ recommendation = `OAuth detected (${oauthButtons.map((b) => b.provider).join(', ')}). OAuth cannot be automated. Run "qulib auth init --base-url ${loginUrl}" to log in manually once and save a reusable storage state file.`;
121
295
  }
122
296
  else if (hasFormLogin) {
123
297
  type = 'form-login';
@@ -140,26 +314,31 @@ export async function detectAuth(url, timeoutMs = 15000, progress) {
140
314
  }
141
315
  else if (hasMagicLink) {
142
316
  type = 'magic-link';
143
- recommendation = `Magic link / passwordless auth detected. Qulib cannot complete email-link flows. Run "qulib auth init --base-url ${url}" to log in manually once and save a storage state file.`;
317
+ recommendation = `Magic link / passwordless auth detected. Qulib cannot complete email-link flows. Run "qulib auth init --base-url ${loginUrl}" to log in manually once and save a storage state file.`;
144
318
  }
145
319
  else if (looksLikeLoginPage) {
146
320
  type = 'unknown';
147
- recommendation = `Authentication required but the pattern is unrecognized. Use "qulib auth init --base-url ${url}" to capture a storage state by logging in manually.`;
321
+ recommendation = `Authentication required but the pattern is unrecognized. Use "qulib auth init --base-url ${loginUrl}" to capture a storage state by logging in manually.`;
148
322
  }
149
323
  else {
150
324
  type = 'none';
151
325
  recommendation = `No authentication required for the entry URL. Qulib can scan anonymously.`;
152
326
  }
327
+ if (clickRevealForms.length > 0) {
328
+ recommendation += `\nAutomatable form login detected via: ${clickRevealForms.map((f) => f.label).join(', ')}. Use type="form-login" with the observed selectors in authOptions.`;
329
+ }
153
330
  const providerList = oauthButtons.length > 0 ? oauthButtons.map((b) => b.provider).join(', ') : provider ?? 'none';
154
- const automatable = type === 'form-login';
331
+ const automatable = type === 'form-login' || clickRevealForms.length > 0;
155
332
  progress?.info(`Auth detected: ${type} (${providerList}) automatable=${automatable}`);
333
+ const authOptions = [...authPathsFromOauthButtons(oauthButtons, loginUrl), ...clickRevealForms];
156
334
  return {
157
- hasAuth: type !== 'none',
335
+ hasAuth: type !== 'none' || oauthButtons.length > 0 || clickRevealForms.length > 0,
158
336
  type,
159
337
  provider,
160
- loginUrl: type === 'none' ? null : loginUrl,
338
+ loginUrl: type === 'none' && oauthButtons.length === 0 && clickRevealForms.length === 0 ? null : loginUrl,
161
339
  observedSelectors,
162
340
  oauthButtons,
341
+ ...(authOptions.length > 0 ? { authOptions } : {}),
163
342
  recommendation,
164
343
  };
165
344
  }
@@ -1 +1 @@
1
- {"version":3,"file":"auth-surface-analyzer.d.ts","sourceRoot":"","sources":["../../src/tools/auth-surface-analyzer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,mCAAmC,CAAC;AAW7D,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,YAAY,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,EAAE,CAAC,CAuJhB"}
1
+ {"version":3,"file":"auth-surface-analyzer.d.ts","sourceRoot":"","sources":["../../src/tools/auth-surface-analyzer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,mCAAmC,CAAC;AAW7D,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,YAAY,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,EAAE,CAAC,CAwKhB"}
@@ -110,16 +110,32 @@ export async function analyzeAuthSurfaceGaps(url, detection, timeoutMs) {
110
110
  const hasEmailLink = await page.getByText(/magic link|email.*link|passwordless/i).count();
111
111
  const hasOAuthUi = detection.oauthButtons.length > 0 ||
112
112
  (await page.getByText(/sign in with|continue with google|microsoft|github/i).count()) > 0;
113
- if (hasOAuthUi && !hasPassword && !hasEmailLink) {
114
- gaps.push({
115
- id: randomUUID(),
116
- path: '/',
117
- severity: 'medium',
118
- category: 'auth-surface',
119
- reason: 'OAuth-only entry with no visible password or magic-link fallback.',
120
- description: 'Users who cannot use a social IdP need another path (email/password, help, or support).',
121
- recommendation: 'Add a documented fallback (email/password, help desk link, or alternate IdP).',
122
- });
113
+ const formLoginFallbacks = (detection.authOptions ?? []).filter((o) => o.type === 'form-login');
114
+ const hasFormLoginFallback = formLoginFallbacks.length > 0;
115
+ if (detection.type === 'oauth' && hasOAuthUi && !hasPassword && !hasEmailLink) {
116
+ if (hasFormLoginFallback) {
117
+ const labels = formLoginFallbacks.map((o) => o.label).join(', ');
118
+ gaps.push({
119
+ id: randomUUID(),
120
+ path: '/',
121
+ severity: 'low',
122
+ category: 'auth-surface',
123
+ reason: `OAuth-primary login with form-login fallback detected via: ${labels}`,
124
+ description: 'A form-based login path exists alongside OAuth. Automate via type="form-login" using the selectors in authOptions.',
125
+ recommendation: `Automatable form option(s): ${labels}. Configure type="form-login" with credentials and selectors from detectedAuth.authOptions.`,
126
+ });
127
+ }
128
+ else {
129
+ gaps.push({
130
+ id: randomUUID(),
131
+ path: '/',
132
+ severity: 'medium',
133
+ category: 'auth-surface',
134
+ reason: 'OAuth-only entry with no visible password or magic-link fallback.',
135
+ description: 'Users who cannot use a social IdP need another path (email/password, help, or support).',
136
+ recommendation: 'Add a documented fallback (email/password, help desk link, or alternate IdP).',
137
+ });
138
+ }
123
139
  }
124
140
  const errorSelectors = '[role="alert"], [data-testid*="error" i], .error, .alert-danger, [class*="error" i]';
125
141
  const errCount = await page.locator(errorSelectors).count();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qulib/core",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Qulib — analyze deployed web apps for honest quality gaps (CLI + programmatic API)",
5
5
  "license": "MIT",
6
6
  "author": "Tapesh Nagarwal",
@@ -13,6 +13,16 @@
13
13
  "bugs": {
14
14
  "url": "https://github.com/TapeshN/qulib/issues"
15
15
  },
16
+ "keywords": [
17
+ "qa",
18
+ "quality",
19
+ "accessibility",
20
+ "gap-analysis",
21
+ "release-confidence",
22
+ "playwright",
23
+ "mcp",
24
+ "ai"
25
+ ],
16
26
  "publishConfig": {
17
27
  "access": "public"
18
28
  },