freestyle-sandboxes 0.1.18 → 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.
- package/cli.mjs +750 -1
- package/index.cjs +817 -816
- package/index.d.cts +652 -634
- package/index.d.mts +652 -634
- package/index.mjs +817 -816
- package/package.json +2 -2
package/cli.mjs
CHANGED
|
@@ -509,4 +509,753 @@ const runCommand = {
|
|
|
509
509
|
}
|
|
510
510
|
};
|
|
511
511
|
|
|
512
|
-
|
|
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();
|