freestyle-sandboxes 0.1.19 → 0.1.20

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 (6) hide show
  1. package/cli.mjs +750 -1
  2. package/index.cjs +817 -816
  3. package/index.d.cts +652 -634
  4. package/index.d.mts +652 -634
  5. package/index.mjs +817 -816
  6. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -509,4 +509,753 @@ const runCommand = {
509
509
  }
510
510
  };
511
511
 
512
- yargs(hideBin(process.argv)).scriptName("freestyle").usage("$0 <command> [options]").command(vmCommand).command(deployCommand).command(runCommand).demandCommand(1, "You need to specify a command").help().alias("help", "h").version().alias("version", "v").strict().parse();
512
+ const gitCommand = {
513
+ command: "git <action>",
514
+ describe: "Manage Git repositories",
515
+ builder: (yargs) => {
516
+ return yargs.command(
517
+ "create",
518
+ "Create a git repository",
519
+ (yargs2) => {
520
+ return yargs2.option("name", {
521
+ alias: "n",
522
+ type: "string",
523
+ description: "Internal repository name"
524
+ }).option("public", {
525
+ type: "boolean",
526
+ description: "Create as public repository",
527
+ default: false
528
+ }).option("default-branch", {
529
+ type: "string",
530
+ description: "Default branch name"
531
+ }).option("source-url", {
532
+ type: "string",
533
+ description: "Fork/import from existing git URL"
534
+ }).option("source-rev", {
535
+ type: "string",
536
+ description: "Revision (branch/tag/sha) for source URL"
537
+ }).option("json", {
538
+ type: "boolean",
539
+ description: "Output as JSON",
540
+ default: false
541
+ });
542
+ },
543
+ async (argv) => {
544
+ loadEnv();
545
+ const args = argv;
546
+ try {
547
+ const freestyle = getFreestyleClient();
548
+ const body = {
549
+ public: args.public
550
+ };
551
+ if (args.name) body.name = args.name;
552
+ if (args.defaultBranch) body.defaultBranch = args.defaultBranch;
553
+ if (args.sourceUrl) {
554
+ body.source = {
555
+ url: args.sourceUrl,
556
+ ...args.sourceRev ? { rev: args.sourceRev } : {}
557
+ };
558
+ }
559
+ console.log("Creating git repository...");
560
+ const result = await freestyle.git.repos.create(body);
561
+ if (args.json) {
562
+ console.log(JSON.stringify(result, null, 2));
563
+ } else {
564
+ console.log("\n\u2713 Repository created successfully!");
565
+ console.log(` Repo ID: ${result.repoId}`);
566
+ console.log(
567
+ ` Clone URL: https://git.freestyle.sh/${result.repoId}`
568
+ );
569
+ }
570
+ } catch (error) {
571
+ handleError(error);
572
+ }
573
+ }
574
+ ).command(
575
+ "list",
576
+ "List git repositories",
577
+ (yargs2) => {
578
+ return yargs2.option("limit", {
579
+ type: "number",
580
+ description: "Maximum repositories to return",
581
+ default: 20
582
+ }).option("cursor", {
583
+ type: "string",
584
+ description: "Offset cursor"
585
+ }).option("json", {
586
+ type: "boolean",
587
+ description: "Output as JSON",
588
+ default: false
589
+ });
590
+ },
591
+ async (argv) => {
592
+ loadEnv();
593
+ const args = argv;
594
+ try {
595
+ const freestyle = getFreestyleClient();
596
+ const repos = await freestyle.git.repos.list({
597
+ limit: args.limit,
598
+ cursor: args.cursor
599
+ });
600
+ if (args.json) {
601
+ console.log(JSON.stringify(repos, null, 2));
602
+ return;
603
+ }
604
+ if (!repos.repositories || repos.repositories.length === 0) {
605
+ console.log("No repositories found.");
606
+ return;
607
+ }
608
+ const rows = repos.repositories.map((repo, idx) => {
609
+ const repoId = repo.repoId || repo.id || "N/A";
610
+ const branchCount = Object.keys(repo.branches || {}).length;
611
+ const tagCount = Object.keys(repo.tags || {}).length;
612
+ return [
613
+ String(idx + 1),
614
+ repoId,
615
+ repo.defaultBranch || "main",
616
+ String(branchCount),
617
+ String(tagCount)
618
+ ];
619
+ });
620
+ formatTable(
621
+ ["#", "Repo ID", "Default Branch", "Branches", "Tags"],
622
+ rows
623
+ );
624
+ console.log(`
625
+ Total: ${repos.total}`);
626
+ console.log(`Offset: ${repos.offset}`);
627
+ } catch (error) {
628
+ handleError(error);
629
+ }
630
+ }
631
+ ).command(
632
+ "delete <repoId>",
633
+ "Delete a git repository",
634
+ (yargs2) => {
635
+ return yargs2.positional("repoId", {
636
+ type: "string",
637
+ description: "Repository ID",
638
+ demandOption: true
639
+ });
640
+ },
641
+ async (argv) => {
642
+ loadEnv();
643
+ const args = argv;
644
+ try {
645
+ const freestyle = getFreestyleClient();
646
+ console.log(`Deleting repository ${args.repoId}...`);
647
+ await freestyle.git.repos.delete({ repoId: args.repoId });
648
+ console.log("\u2713 Repository deleted");
649
+ } catch (error) {
650
+ handleError(error);
651
+ }
652
+ }
653
+ ).demandCommand(1, "You need to specify a git action");
654
+ },
655
+ handler: () => {
656
+ }
657
+ };
658
+
659
+ const domainsCommand = {
660
+ command: "domains <action>",
661
+ describe: "Manage domains, verifications, and mappings",
662
+ builder: (yargs) => {
663
+ return yargs.command(
664
+ "list",
665
+ "List verified domains",
666
+ (yargs2) => {
667
+ return yargs2.option("limit", {
668
+ type: "number",
669
+ description: "Maximum domains to return",
670
+ default: 50
671
+ }).option("cursor", {
672
+ type: "string",
673
+ description: "Offset cursor"
674
+ }).option("json", {
675
+ type: "boolean",
676
+ description: "Output as JSON",
677
+ default: false
678
+ });
679
+ },
680
+ async (argv) => {
681
+ loadEnv();
682
+ const args = argv;
683
+ try {
684
+ const freestyle = getFreestyleClient();
685
+ const domains = await freestyle.domains.list({
686
+ limit: args.limit,
687
+ cursor: args.cursor
688
+ });
689
+ if (args.json) {
690
+ console.log(JSON.stringify(domains, null, 2));
691
+ return;
692
+ }
693
+ if (domains.length === 0) {
694
+ console.log("No verified domains found.");
695
+ return;
696
+ }
697
+ const rows = domains.map((domain) => [
698
+ domain.domain,
699
+ domain.verifiedDns ? "yes" : "no",
700
+ domain.createdAt ? new Date(domain.createdAt).toLocaleString() : "N/A"
701
+ ]);
702
+ formatTable(["Domain", "DNS Verified", "Created"], rows);
703
+ } catch (error) {
704
+ handleError(error);
705
+ }
706
+ }
707
+ ).command(
708
+ "verify <domain>",
709
+ "Create a domain verification request",
710
+ (yargs2) => {
711
+ return yargs2.positional("domain", {
712
+ type: "string",
713
+ description: "Domain to verify",
714
+ demandOption: true
715
+ }).option("json", {
716
+ type: "boolean",
717
+ description: "Output as JSON",
718
+ default: false
719
+ });
720
+ },
721
+ async (argv) => {
722
+ loadEnv();
723
+ const args = argv;
724
+ try {
725
+ const freestyle = getFreestyleClient();
726
+ const result = await freestyle.domains.verifications.create({
727
+ domain: args.domain
728
+ });
729
+ if (args.json) {
730
+ console.log(JSON.stringify(result, null, 2));
731
+ return;
732
+ }
733
+ console.log("\n\u2713 Verification created!");
734
+ console.log(` Verification ID: ${result.verificationId}`);
735
+ console.log("\nAdd this DNS record:");
736
+ console.log(` Type: ${result.record.type}`);
737
+ console.log(` Name: ${result.record.name}`);
738
+ console.log(` Value: ${result.record.value}`);
739
+ } catch (error) {
740
+ handleError(error);
741
+ }
742
+ }
743
+ ).command(
744
+ "complete",
745
+ "Complete a domain verification",
746
+ (yargs2) => {
747
+ return yargs2.option("domain", {
748
+ type: "string",
749
+ description: "Domain to complete verification for"
750
+ }).option("verification-id", {
751
+ type: "string",
752
+ description: "Verification ID to complete"
753
+ }).option("json", {
754
+ type: "boolean",
755
+ description: "Output as JSON",
756
+ default: false
757
+ }).check((argv) => {
758
+ const hasDomain = !!argv.domain;
759
+ const hasVerificationId = !!argv["verification-id"];
760
+ if (!hasDomain && !hasVerificationId) {
761
+ throw new Error("Specify one of --domain or --verification-id");
762
+ }
763
+ if (hasDomain && hasVerificationId) {
764
+ throw new Error(
765
+ "Specify only one of --domain or --verification-id"
766
+ );
767
+ }
768
+ return true;
769
+ });
770
+ },
771
+ async (argv) => {
772
+ loadEnv();
773
+ const args = argv;
774
+ try {
775
+ const freestyle = getFreestyleClient();
776
+ const result = args.verificationId ? await freestyle.domains.verifications.complete({
777
+ verificationId: args.verificationId
778
+ }) : await freestyle.domains.verifications.complete({
779
+ domain: args.domain
780
+ });
781
+ if (args.json) {
782
+ console.log(JSON.stringify(result, null, 2));
783
+ } else {
784
+ console.log("\u2713 Domain verification completed");
785
+ console.log(` Domain: ${result.domain}`);
786
+ }
787
+ } catch (error) {
788
+ handleError(error);
789
+ }
790
+ }
791
+ ).command(
792
+ "verifications",
793
+ "List pending/verifiable domain verifications",
794
+ (yargs2) => {
795
+ return yargs2.option("json", {
796
+ type: "boolean",
797
+ description: "Output as JSON",
798
+ default: false
799
+ });
800
+ },
801
+ async (argv) => {
802
+ loadEnv();
803
+ const args = argv;
804
+ try {
805
+ const freestyle = getFreestyleClient();
806
+ const verifications = await freestyle.domains.verifications.list();
807
+ if (args.json) {
808
+ console.log(JSON.stringify(verifications, null, 2));
809
+ return;
810
+ }
811
+ if (verifications.length === 0) {
812
+ console.log("No verifications found.");
813
+ return;
814
+ }
815
+ const rows = verifications.map((verification) => [
816
+ verification.domain,
817
+ verification.verificationCode,
818
+ new Date(verification.createdAt).toLocaleString()
819
+ ]);
820
+ formatTable(["Domain", "Verification Code", "Created"], rows);
821
+ } catch (error) {
822
+ handleError(error);
823
+ }
824
+ }
825
+ ).command(
826
+ "map <domain>",
827
+ "Create a domain mapping",
828
+ (yargs2) => {
829
+ return yargs2.positional("domain", {
830
+ type: "string",
831
+ description: "Domain to map",
832
+ demandOption: true
833
+ }).option("deployment-id", {
834
+ type: "string",
835
+ description: "Deployment ID target"
836
+ }).option("vm-id", {
837
+ type: "string",
838
+ description: "VM ID target"
839
+ }).option("vm-port", {
840
+ type: "number",
841
+ description: "VM port target (required with --vm-id)"
842
+ }).option("json", {
843
+ type: "boolean",
844
+ description: "Output as JSON",
845
+ default: false
846
+ }).check((argv) => {
847
+ const hasDeploymentId = !!argv["deployment-id"];
848
+ const hasVmId = !!argv["vm-id"];
849
+ const hasVmPort = typeof argv["vm-port"] === "number";
850
+ if (hasDeploymentId && hasVmId) {
851
+ throw new Error(
852
+ "Specify either --deployment-id or --vm-id (not both)"
853
+ );
854
+ }
855
+ if (!hasDeploymentId && !hasVmId) {
856
+ throw new Error("Specify one of --deployment-id or --vm-id");
857
+ }
858
+ if (hasVmId && !hasVmPort) {
859
+ throw new Error("--vm-port is required when using --vm-id");
860
+ }
861
+ return true;
862
+ });
863
+ },
864
+ async (argv) => {
865
+ loadEnv();
866
+ const args = argv;
867
+ try {
868
+ const freestyle = getFreestyleClient();
869
+ const result = args.deploymentId ? await freestyle.domains.mappings.create({
870
+ domain: args.domain,
871
+ deploymentId: args.deploymentId
872
+ }) : await freestyle.domains.mappings.create({
873
+ domain: args.domain,
874
+ vmId: args.vmId,
875
+ vmPort: args.vmPort
876
+ });
877
+ if (args.json) {
878
+ console.log(JSON.stringify(result, null, 2));
879
+ } else {
880
+ console.log("\u2713 Domain mapping created");
881
+ console.log(` Domain: ${result.domain}`);
882
+ if (result.deploymentId) {
883
+ console.log(` Deployment ID: ${result.deploymentId}`);
884
+ }
885
+ if (result.vmId) {
886
+ console.log(` VM ID: ${result.vmId}`);
887
+ }
888
+ if (result.vmPort) {
889
+ console.log(` VM Port: ${result.vmPort}`);
890
+ }
891
+ }
892
+ } catch (error) {
893
+ handleError(error);
894
+ }
895
+ }
896
+ ).command(
897
+ "unmap <domain>",
898
+ "Delete a domain mapping",
899
+ (yargs2) => {
900
+ return yargs2.positional("domain", {
901
+ type: "string",
902
+ description: "Domain to unmap",
903
+ demandOption: true
904
+ });
905
+ },
906
+ async (argv) => {
907
+ loadEnv();
908
+ const args = argv;
909
+ try {
910
+ const freestyle = getFreestyleClient();
911
+ await freestyle.domains.mappings.delete({ domain: args.domain });
912
+ console.log("\u2713 Domain mapping deleted");
913
+ } catch (error) {
914
+ handleError(error);
915
+ }
916
+ }
917
+ ).command(
918
+ "mappings",
919
+ "List domain mappings",
920
+ (yargs2) => {
921
+ return yargs2.option("domain", {
922
+ type: "string",
923
+ description: "Filter by domain"
924
+ }).option("limit", {
925
+ type: "number",
926
+ description: "Maximum mappings to return",
927
+ default: 50
928
+ }).option("cursor", {
929
+ type: "string",
930
+ description: "Offset cursor"
931
+ }).option("json", {
932
+ type: "boolean",
933
+ description: "Output as JSON",
934
+ default: false
935
+ });
936
+ },
937
+ async (argv) => {
938
+ loadEnv();
939
+ const args = argv;
940
+ try {
941
+ const freestyle = getFreestyleClient();
942
+ const { mappings } = await freestyle.domains.mappings.list({
943
+ domain: args.domain,
944
+ limit: args.limit,
945
+ cursor: args.cursor
946
+ });
947
+ if (args.json) {
948
+ console.log(JSON.stringify(mappings, null, 2));
949
+ return;
950
+ }
951
+ if (mappings.length === 0) {
952
+ console.log("No domain mappings found.");
953
+ return;
954
+ }
955
+ const rows = mappings.map((mapping) => [
956
+ mapping.domain,
957
+ mapping.deploymentId || "-",
958
+ mapping.vmId || "-",
959
+ mapping.vmPort != null ? String(mapping.vmPort) : "-",
960
+ new Date(mapping.createdAt).toLocaleString()
961
+ ]);
962
+ formatTable(
963
+ ["Domain", "Deployment", "VM", "VM Port", "Created"],
964
+ rows
965
+ );
966
+ } catch (error) {
967
+ handleError(error);
968
+ }
969
+ }
970
+ ).demandCommand(1, "You need to specify a domains action");
971
+ },
972
+ handler: () => {
973
+ }
974
+ };
975
+
976
+ async function getCronJobById(scheduleId) {
977
+ const freestyle = getFreestyleClient();
978
+ const { jobs } = await freestyle.cron.list();
979
+ const job = jobs.find((candidate) => candidate.schedule.id === scheduleId);
980
+ if (!job) {
981
+ throw new Error(`Cron schedule not found: ${scheduleId}`);
982
+ }
983
+ return job;
984
+ }
985
+ const cronCommand = {
986
+ command: "cron <action>",
987
+ describe: "Manage cron schedules for deployments",
988
+ builder: (yargs) => {
989
+ return yargs.command(
990
+ "schedule",
991
+ "Create a cron schedule",
992
+ (yargs2) => {
993
+ return yargs2.option("deployment-id", {
994
+ type: "string",
995
+ description: "Deployment ID to schedule",
996
+ demandOption: true
997
+ }).option("cron", {
998
+ type: "string",
999
+ description: "Cron expression",
1000
+ demandOption: true
1001
+ }).option("timezone", {
1002
+ type: "string",
1003
+ description: "Timezone (default: UTC)",
1004
+ default: "UTC"
1005
+ }).option("payload", {
1006
+ type: "string",
1007
+ description: "JSON payload string passed to scheduled handler"
1008
+ }).option("path", {
1009
+ type: "string",
1010
+ description: "Optional path for scheduled trigger"
1011
+ }).option("json", {
1012
+ type: "boolean",
1013
+ description: "Output as JSON",
1014
+ default: false
1015
+ });
1016
+ },
1017
+ async (argv) => {
1018
+ loadEnv();
1019
+ const args = argv;
1020
+ try {
1021
+ const freestyle = getFreestyleClient();
1022
+ let parsedPayload = {};
1023
+ if (args.payload) {
1024
+ try {
1025
+ parsedPayload = JSON.parse(args.payload);
1026
+ } catch {
1027
+ throw new Error("--payload must be valid JSON");
1028
+ }
1029
+ }
1030
+ const { job } = await freestyle.cron.schedule({
1031
+ deploymentId: args.deploymentId,
1032
+ cron: args.cron,
1033
+ timezone: args.timezone,
1034
+ payload: parsedPayload,
1035
+ path: args.path
1036
+ });
1037
+ if (args.json) {
1038
+ console.log(JSON.stringify(job.schedule, null, 2));
1039
+ } else {
1040
+ console.log("\u2713 Cron schedule created");
1041
+ console.log(` Schedule ID: ${job.schedule.id}`);
1042
+ console.log(` Deployment ID: ${job.schedule.deploymentId}`);
1043
+ console.log(` Cron: ${job.schedule.cron}`);
1044
+ console.log(` Timezone: ${job.schedule.timezone}`);
1045
+ console.log(` Active: ${job.schedule.active ? "yes" : "no"}`);
1046
+ }
1047
+ } catch (error) {
1048
+ handleError(error);
1049
+ }
1050
+ }
1051
+ ).command(
1052
+ "list",
1053
+ "List cron schedules",
1054
+ (yargs2) => {
1055
+ return yargs2.option("deployment-id", {
1056
+ type: "string",
1057
+ description: "Filter by deployment ID"
1058
+ }).option("json", {
1059
+ type: "boolean",
1060
+ description: "Output as JSON",
1061
+ default: false
1062
+ });
1063
+ },
1064
+ async (argv) => {
1065
+ loadEnv();
1066
+ const args = argv;
1067
+ try {
1068
+ const freestyle = getFreestyleClient();
1069
+ const { jobs } = await freestyle.cron.list({
1070
+ deploymentId: args.deploymentId
1071
+ });
1072
+ if (args.json) {
1073
+ console.log(
1074
+ JSON.stringify(
1075
+ jobs.map((job) => job.schedule),
1076
+ null,
1077
+ 2
1078
+ )
1079
+ );
1080
+ return;
1081
+ }
1082
+ if (jobs.length === 0) {
1083
+ console.log("No cron schedules found.");
1084
+ return;
1085
+ }
1086
+ const rows = jobs.map((job) => [
1087
+ job.schedule.id,
1088
+ job.schedule.deploymentId,
1089
+ job.schedule.cron,
1090
+ job.schedule.timezone,
1091
+ job.schedule.active ? "active" : "disabled"
1092
+ ]);
1093
+ formatTable(
1094
+ ["Schedule ID", "Deployment", "Cron", "Timezone", "Status"],
1095
+ rows
1096
+ );
1097
+ } catch (error) {
1098
+ handleError(error);
1099
+ }
1100
+ }
1101
+ ).command(
1102
+ "enable <scheduleId>",
1103
+ "Enable a cron schedule",
1104
+ (yargs2) => {
1105
+ return yargs2.positional("scheduleId", {
1106
+ type: "string",
1107
+ description: "Schedule ID",
1108
+ demandOption: true
1109
+ });
1110
+ },
1111
+ async (argv) => {
1112
+ loadEnv();
1113
+ const args = argv;
1114
+ try {
1115
+ const job = await getCronJobById(args.scheduleId);
1116
+ await job.enable();
1117
+ console.log(`\u2713 Enabled schedule ${args.scheduleId}`);
1118
+ } catch (error) {
1119
+ handleError(error);
1120
+ }
1121
+ }
1122
+ ).command(
1123
+ "disable <scheduleId>",
1124
+ "Disable a cron schedule",
1125
+ (yargs2) => {
1126
+ return yargs2.positional("scheduleId", {
1127
+ type: "string",
1128
+ description: "Schedule ID",
1129
+ demandOption: true
1130
+ });
1131
+ },
1132
+ async (argv) => {
1133
+ loadEnv();
1134
+ const args = argv;
1135
+ try {
1136
+ const job = await getCronJobById(args.scheduleId);
1137
+ await job.disable();
1138
+ console.log(`\u2713 Disabled schedule ${args.scheduleId}`);
1139
+ } catch (error) {
1140
+ handleError(error);
1141
+ }
1142
+ }
1143
+ ).command(
1144
+ "executions <scheduleId>",
1145
+ "List executions for a cron schedule",
1146
+ (yargs2) => {
1147
+ return yargs2.positional("scheduleId", {
1148
+ type: "string",
1149
+ description: "Schedule ID",
1150
+ demandOption: true
1151
+ }).option("limit", {
1152
+ type: "number",
1153
+ description: "Maximum executions to return",
1154
+ default: 20
1155
+ }).option("cursor", {
1156
+ type: "string",
1157
+ description: "Offset cursor"
1158
+ }).option("json", {
1159
+ type: "boolean",
1160
+ description: "Output as JSON",
1161
+ default: false
1162
+ });
1163
+ },
1164
+ async (argv) => {
1165
+ loadEnv();
1166
+ const args = argv;
1167
+ try {
1168
+ const job = await getCronJobById(args.scheduleId);
1169
+ const result = await job.executions({
1170
+ limit: args.limit,
1171
+ cursor: args.cursor
1172
+ });
1173
+ if (args.json) {
1174
+ console.log(JSON.stringify(result, null, 2));
1175
+ return;
1176
+ }
1177
+ if (result.executions.length === 0) {
1178
+ console.log("No executions found.");
1179
+ return;
1180
+ }
1181
+ const rows = result.executions.map((execution) => [
1182
+ execution.id,
1183
+ execution.status,
1184
+ execution.attempts.toString(),
1185
+ execution.runAt,
1186
+ execution.lastError || "-"
1187
+ ]);
1188
+ formatTable(
1189
+ ["Execution ID", "Status", "Attempts", "Run At", "Last Error"],
1190
+ rows
1191
+ );
1192
+ const byStatus = result.executions.reduce(
1193
+ (acc, execution) => {
1194
+ acc[execution.status] = (acc[execution.status] || 0) + 1;
1195
+ return acc;
1196
+ },
1197
+ {}
1198
+ );
1199
+ console.log("\nSummary:");
1200
+ console.log(` queued: ${byStatus.queued || 0}`);
1201
+ console.log(` running: ${byStatus.running || 0}`);
1202
+ console.log(` succeeded: ${byStatus.succeeded || 0}`);
1203
+ console.log(` failed: ${byStatus.failed || 0}`);
1204
+ console.log(` retry: ${byStatus.retry || 0}`);
1205
+ } catch (error) {
1206
+ handleError(error);
1207
+ }
1208
+ }
1209
+ ).command(
1210
+ "success-rate <scheduleId>",
1211
+ "Get success rate for a cron schedule over a time range",
1212
+ (yargs2) => {
1213
+ return yargs2.positional("scheduleId", {
1214
+ type: "string",
1215
+ description: "Schedule ID",
1216
+ demandOption: true
1217
+ }).option("start", {
1218
+ type: "string",
1219
+ description: "Range start (ISO datetime)",
1220
+ demandOption: true
1221
+ }).option("end", {
1222
+ type: "string",
1223
+ description: "Range end (ISO datetime)",
1224
+ demandOption: true
1225
+ }).option("json", {
1226
+ type: "boolean",
1227
+ description: "Output as JSON",
1228
+ default: false
1229
+ });
1230
+ },
1231
+ async (argv) => {
1232
+ loadEnv();
1233
+ const args = argv;
1234
+ try {
1235
+ const job = await getCronJobById(args.scheduleId);
1236
+ const result = await job.successRate({
1237
+ start: args.start,
1238
+ end: args.end
1239
+ });
1240
+ if (args.json) {
1241
+ console.log(JSON.stringify(result, null, 2));
1242
+ } else {
1243
+ console.log("Cron success rate");
1244
+ console.log(` Schedule ID: ${args.scheduleId}`);
1245
+ console.log(` Range: ${result.start} -> ${result.end}`);
1246
+ console.log(` Total: ${result.total}`);
1247
+ console.log(` Succeeded: ${result.succeeded}`);
1248
+ console.log(` Failed: ${result.failed}`);
1249
+ console.log(` Success Rate: ${result.successRate}`);
1250
+ }
1251
+ } catch (error) {
1252
+ handleError(error);
1253
+ }
1254
+ }
1255
+ ).demandCommand(1, "You need to specify a cron action");
1256
+ },
1257
+ handler: () => {
1258
+ }
1259
+ };
1260
+
1261
+ yargs(hideBin(process.argv)).scriptName("freestyle").usage("$0 <command> [options]").command(vmCommand).command(gitCommand).command(domainsCommand).command(cronCommand).command(deployCommand).command(runCommand).demandCommand(1, "You need to specify a command").help().alias("help", "h").version().alias("version", "v").strict().parse();