@lizard-build/sdk 0.1.72 → 0.1.73

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/dist/index.mjs CHANGED
@@ -125,9 +125,9 @@ async function handleApiError(res) {
125
125
  // src/platform/client.ts
126
126
  var segment = (value) => encodeURIComponent(value);
127
127
  function query(path, values) {
128
- const qs = new URLSearchParams();
129
- for (const [key, value] of Object.entries(values)) if (value !== void 0) qs.set(key, String(value));
130
- return qs.size ? `${path}?${qs}` : path;
128
+ const qs2 = new URLSearchParams();
129
+ for (const [key, value] of Object.entries(values)) if (value !== void 0) qs2.set(key, String(value));
130
+ return qs2.size ? `${path}?${qs2}` : path;
131
131
  }
132
132
  var PlatformClient = class {
133
133
  constructor(opts) {
@@ -481,6 +481,525 @@ var GitHubAPI = class {
481
481
  }
482
482
  };
483
483
 
484
+ // src/platform/git.ts
485
+ var DEFAULT_GIT_URL = "https://git.lizard.build";
486
+ var seg = encodeURIComponent;
487
+ function qs(path, values) {
488
+ const q = new URLSearchParams();
489
+ for (const [k, v] of Object.entries(values)) if (v !== void 0 && v !== "") q.set(k, String(v));
490
+ return q.size ? `${path}?${q}` : path;
491
+ }
492
+ function repoPath(repo) {
493
+ const m = /^([^/\s]+)\/([^/\s]+?)(\.git)?$/.exec(repo);
494
+ if (!m) throw new Error(`repository must be "owner/name", got "${repo}"`);
495
+ return `/api/repos/${seg(m[1])}/${seg(m[2])}`;
496
+ }
497
+ var GitClient = class {
498
+ constructor(config, url) {
499
+ this.config = config;
500
+ this.url = (url ?? process.env.LIZARD_GIT_URL ?? DEFAULT_GIT_URL).replace(/\/+$/, "");
501
+ }
502
+ async raw(method, path, body, headers = {}) {
503
+ const res = await fetch(this.url + path, {
504
+ method,
505
+ body,
506
+ headers: { "X-API-Key": this.config.apiKey, ...headers },
507
+ signal: AbortSignal.timeout(this.config.requestTimeoutMs)
508
+ });
509
+ if (!res.ok) await handleApiError(res);
510
+ return res;
511
+ }
512
+ async request(method, path, body, headers = {}) {
513
+ const res = await this.raw(
514
+ method,
515
+ path,
516
+ body === void 0 ? void 0 : JSON.stringify(body),
517
+ body === void 0 ? headers : { "Content-Type": "application/json", ...headers }
518
+ );
519
+ const text = res.status === 204 ? "" : await res.text();
520
+ return text ? JSON.parse(text) : void 0;
521
+ }
522
+ get(path) {
523
+ return this.request("GET", path);
524
+ }
525
+ post(path, body) {
526
+ return this.request("POST", path, body ?? {});
527
+ }
528
+ put(path, body) {
529
+ return this.request("PUT", path, body ?? {});
530
+ }
531
+ patch(path, body) {
532
+ return this.request("PATCH", path, body);
533
+ }
534
+ delete(path) {
535
+ return this.request("DELETE", path);
536
+ }
537
+ async bytes(path) {
538
+ return new Uint8Array(await (await this.raw("GET", path)).arrayBuffer());
539
+ }
540
+ };
541
+ var GitReposAPI = class {
542
+ constructor(c) {
543
+ this.c = c;
544
+ }
545
+ /** Yours and the ones you collaborate on by default; `owner`, `q`, `starred` narrow it. */
546
+ list(opts = {}) {
547
+ return this.c.get(qs("/api/repos", opts));
548
+ }
549
+ /** Answers the new repository's `owner` and `name`. */
550
+ create(opts) {
551
+ return this.c.post("/api/repos", opts);
552
+ }
553
+ get(repo) {
554
+ return this.c.get(repoPath(repo));
555
+ }
556
+ update(repo, opts) {
557
+ return this.c.patch(repoPath(repo), opts);
558
+ }
559
+ delete(repo) {
560
+ return this.c.delete(repoPath(repo));
561
+ }
562
+ star(repo) {
563
+ return this.c.put(`${repoPath(repo)}/star`);
564
+ }
565
+ unstar(repo) {
566
+ return this.c.delete(`${repoPath(repo)}/star`);
567
+ }
568
+ /** Fork into your account (`name` defaults to the same name). */
569
+ fork(repo, opts = {}) {
570
+ return this.c.post(`${repoPath(repo)}/forks`, opts);
571
+ }
572
+ forks(repo) {
573
+ return this.c.get(`${repoPath(repo)}/forks`);
574
+ }
575
+ /** Branches and tags. */
576
+ refs(repo) {
577
+ return this.c.get(`${repoPath(repo)}/refs`);
578
+ }
579
+ createBranch(repo, name, from) {
580
+ return this.c.post(`${repoPath(repo)}/branches`, { name, from });
581
+ }
582
+ deleteBranch(repo, name) {
583
+ return this.c.delete(`${repoPath(repo)}/branches/${name.split("/").map(seg).join("/")}`);
584
+ }
585
+ tree(repo, opts = {}) {
586
+ return this.c.get(qs(`${repoPath(repo)}/tree`, opts));
587
+ }
588
+ /** A file as JSON (text in `content`). */
589
+ file(repo, path, ref) {
590
+ return this.c.get(qs(`${repoPath(repo)}/blob`, { path, ref }));
591
+ }
592
+ /** A file's bytes, whatever its size or type. */
593
+ raw(repo, path, ref) {
594
+ const [owner, name] = repo.split("/");
595
+ return this.c.bytes(`/raw/${seg(owner)}/${seg(name)}/${ref.split("/").map(seg).join("/")}/${path.split("/").map(seg).join("/")}`);
596
+ }
597
+ /** Every file path at a ref. */
598
+ files(repo, ref) {
599
+ return this.c.get(qs(`${repoPath(repo)}/files`, { ref }));
600
+ }
601
+ search(repo, q, ref) {
602
+ return this.c.get(qs(`${repoPath(repo)}/search`, { q, ref }));
603
+ }
604
+ commits(repo, opts = {}) {
605
+ return this.c.get(qs(`${repoPath(repo)}/commits`, opts));
606
+ }
607
+ commit(repo, sha) {
608
+ return this.c.get(`${repoPath(repo)}/commits/${seg(sha)}`);
609
+ }
610
+ compare(repo, base, head) {
611
+ return this.c.get(qs(`${repoPath(repo)}/compare`, { base, head }));
612
+ }
613
+ /** Commit files on the server: no clone, no push. Answers 409 when `expect` no longer matches. */
614
+ commitFiles(repo, opts) {
615
+ const { idempotencyKey, ...body } = opts;
616
+ return this.c.request("POST", `${repoPath(repo)}/commits`, body, idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {});
617
+ }
618
+ /** Rebase `head` (branch or commit) onto `onto` on the server and move `onto` there; retried on races. */
619
+ land(repo, opts) {
620
+ return this.c.post(`${repoPath(repo)}/land`, opts);
621
+ }
622
+ /** Who moved which branch, when. */
623
+ reflog(repo, ref) {
624
+ return this.c.get(qs(`${repoPath(repo)}/reflog`, { ref }));
625
+ }
626
+ archive(repo, ref, format = "tar.gz") {
627
+ return this.c.bytes(`${repoPath(repo)}/archive/${seg(ref)}.${format}`);
628
+ }
629
+ /** The HTTPS clone URL. With `withKey`, it carries the API key, so plain `git clone` works without a helper. */
630
+ cloneUrl(repo, opts = {}) {
631
+ repoPath(repo);
632
+ const u = new URL(`${this.c.url}/${repo.replace(/\.git$/, "")}.git`);
633
+ if (opts.withKey) {
634
+ u.username = "lizard";
635
+ u.password = this.c.config.apiKey;
636
+ }
637
+ return u.toString();
638
+ }
639
+ };
640
+ var GitIssuesAPI = class {
641
+ constructor(c, pulls = false) {
642
+ this.c = c;
643
+ this.pulls = pulls;
644
+ }
645
+ base(repo) {
646
+ return `${repoPath(repo)}/issues`;
647
+ }
648
+ list(repo, opts = {}) {
649
+ return this.c.get(qs(this.base(repo), { ...opts, pulls: this.pulls ? 1 : void 0 }));
650
+ }
651
+ get(repo, number) {
652
+ return this.c.get(`${this.base(repo)}/${number}`);
653
+ }
654
+ update(repo, number, opts) {
655
+ return this.c.patch(`${this.base(repo)}/${number}`, opts);
656
+ }
657
+ comment(repo, number, body) {
658
+ return this.c.post(`${this.base(repo)}/${number}/comments`, { body });
659
+ }
660
+ close(repo, number) {
661
+ return this.update(repo, number, { state: "closed" });
662
+ }
663
+ reopen(repo, number) {
664
+ return this.update(repo, number, { state: "open" });
665
+ }
666
+ };
667
+ var GitIssuesOnlyAPI = class extends GitIssuesAPI {
668
+ constructor(c) {
669
+ super(c, false);
670
+ }
671
+ create(repo, opts) {
672
+ return this.c.post(this.base(repo), opts);
673
+ }
674
+ };
675
+ var GitPullsAPI = class extends GitIssuesAPI {
676
+ constructor(c) {
677
+ super(c, true);
678
+ }
679
+ create(repo, opts) {
680
+ return this.c.post(this.base(repo), opts);
681
+ }
682
+ /** Mark a draft ready for review. */
683
+ ready(repo, number) {
684
+ return this.update(repo, number, { draft: false });
685
+ }
686
+ merge(repo, number, opts = {}) {
687
+ return this.c.post(`${repoPath(repo)}/pulls/${number}/merge`, opts);
688
+ }
689
+ diff(repo, number) {
690
+ return this.c.get(`${repoPath(repo)}/pulls/${number}/diff`);
691
+ }
692
+ commits(repo, number) {
693
+ return this.c.get(`${repoPath(repo)}/pulls/${number}/commits`);
694
+ }
695
+ reviews(repo, number) {
696
+ return this.c.get(`${repoPath(repo)}/pulls/${number}/reviews`);
697
+ }
698
+ review(repo, number, opts) {
699
+ return this.c.post(`${repoPath(repo)}/pulls/${number}/reviews`, opts);
700
+ }
701
+ approve(repo, number, body = "") {
702
+ return this.review(repo, number, { event: "APPROVE", body });
703
+ }
704
+ requestChanges(repo, number, body) {
705
+ return this.review(repo, number, { event: "REQUEST_CHANGES", body });
706
+ }
707
+ lineComment(repo, number, c) {
708
+ return this.c.post(`${repoPath(repo)}/pulls/${number}/comments`, c);
709
+ }
710
+ deleteLineComment(repo, number, id) {
711
+ return this.c.delete(`${repoPath(repo)}/pulls/${number}/comments/${id}`);
712
+ }
713
+ /** The combined commit status of the pull request's head (CI and external checks). */
714
+ async checks(repo, number) {
715
+ const { pull } = await this.get(repo, number);
716
+ if (!pull?.headSha) throw new Error(`pull request #${number} has no head commit`);
717
+ return this.c.get(`${repoPath(repo)}/commits/${pull.headSha}/status`);
718
+ }
719
+ };
720
+ var GitSecretsAPI = class {
721
+ constructor(c) {
722
+ this.c = c;
723
+ }
724
+ list(repo) {
725
+ return this.c.get(`${repoPath(repo)}/actions/secrets`);
726
+ }
727
+ set(repo, name, value) {
728
+ return this.c.put(`${repoPath(repo)}/actions/secrets/${seg(name)}`, { value });
729
+ }
730
+ delete(repo, name) {
731
+ return this.c.delete(`${repoPath(repo)}/actions/secrets/${seg(name)}`);
732
+ }
733
+ };
734
+ var GitActionsAPI = class {
735
+ constructor(c) {
736
+ this.c = c;
737
+ this.secrets = new GitSecretsAPI(c);
738
+ }
739
+ runs(repo, opts = {}) {
740
+ return this.c.get(qs(`${repoPath(repo)}/actions/runs`, { ...opts }));
741
+ }
742
+ run(repo, number) {
743
+ return this.c.get(`${repoPath(repo)}/actions/runs/${number}`);
744
+ }
745
+ cancel(repo, number) {
746
+ return this.c.post(`${repoPath(repo)}/actions/runs/${number}/cancel`);
747
+ }
748
+ /** Run the same workflow at the same commit again (a new run). */
749
+ rerun(repo, number) {
750
+ return this.c.post(`${repoPath(repo)}/actions/runs/${number}/rerun`);
751
+ }
752
+ /** Let a run from a fork's pull request start (it waits for someone with write access). */
753
+ approve(repo, number) {
754
+ return this.c.post(`${repoPath(repo)}/actions/runs/${number}/approve`);
755
+ }
756
+ /** Part of a job's log from `offset`; follow `next` until `done`. */
757
+ log(repo, jobId, offset = 0) {
758
+ return this.c.get(qs(`${repoPath(repo)}/actions/jobs/${jobId}/log`, { offset }));
759
+ }
760
+ async fullLog(repo, jobId) {
761
+ return new TextDecoder().decode(await this.c.bytes(`${repoPath(repo)}/actions/jobs/${jobId}/log.txt`));
762
+ }
763
+ /** Each new piece of the log as it's written, until the job ends. */
764
+ async *followLog(repo, jobId, opts = {}) {
765
+ let offset = 0;
766
+ while (!opts.signal?.aborted) {
767
+ const chunk = await this.log(repo, jobId, offset);
768
+ if (chunk.text) yield chunk.text;
769
+ offset = chunk.next;
770
+ if (chunk.done && offset >= chunk.size) return;
771
+ if (!chunk.text) await new Promise((r) => setTimeout(r, opts.intervalMs ?? 1500));
772
+ }
773
+ }
774
+ /** Wait until the run ends (success, failure, cancelled, ...). */
775
+ async wait(repo, number, opts = {}) {
776
+ const until = Date.now() + (opts.timeoutMs ?? 30 * 6e4);
777
+ for (; ; ) {
778
+ const run = await this.run(repo, number);
779
+ if (!["queued", "running", "waiting", "pending"].includes(run.status)) return run;
780
+ if (Date.now() > until) throw new Error(`run ${number} is still ${run.status}`);
781
+ await new Promise((r) => setTimeout(r, opts.intervalMs ?? 3e3));
782
+ }
783
+ }
784
+ artifacts(repo, number) {
785
+ return this.c.get(`${repoPath(repo)}/actions/runs/${number}/artifacts`);
786
+ }
787
+ /** An artifact as a .tar.gz. */
788
+ downloadArtifact(repo, number, name) {
789
+ return this.c.bytes(`${repoPath(repo)}/actions/runs/${number}/artifacts/${seg(name)}`);
790
+ }
791
+ workflows(repo) {
792
+ return this.c.get(`${repoPath(repo)}/actions/workflows`);
793
+ }
794
+ /** Start a workflow that has `on: workflow_dispatch`. */
795
+ dispatch(repo, opts) {
796
+ return this.c.post(`${repoPath(repo)}/actions/workflows/dispatch`, opts);
797
+ }
798
+ /** Latest run status per commit, for the given commits. */
799
+ status(repo, shas) {
800
+ return this.c.get(qs(`${repoPath(repo)}/actions/status`, { shas: shas.join(",") }));
801
+ }
802
+ };
803
+ var GitCollaboratorsAPI = class {
804
+ constructor(c) {
805
+ this.c = c;
806
+ }
807
+ list(repo) {
808
+ return this.c.get(`${repoPath(repo)}/collaborators`);
809
+ }
810
+ /** Invite someone with a Lizard Git account (they sign in once first); they're in once they accept. */
811
+ invite(repo, login, role = "write") {
812
+ return this.c.post(`${repoPath(repo)}/collaborators`, { login, role });
813
+ }
814
+ setRole(repo, login, role) {
815
+ return this.c.patch(`${repoPath(repo)}/collaborators/${seg(login)}`, { role });
816
+ }
817
+ /** Remove someone (or yourself: leave). */
818
+ remove(repo, login) {
819
+ return this.c.delete(`${repoPath(repo)}/collaborators/${seg(login)}`);
820
+ }
821
+ cancelInvitation(repo, id) {
822
+ return this.c.delete(`${repoPath(repo)}/invitations/${id}`);
823
+ }
824
+ /** Invitations waiting for you. */
825
+ invitations() {
826
+ return this.c.get("/api/user/invitations");
827
+ }
828
+ accept(id) {
829
+ return this.c.post(`/api/user/invitations/${id}/accept`);
830
+ }
831
+ decline(id) {
832
+ return this.c.post(`/api/user/invitations/${id}/decline`);
833
+ }
834
+ };
835
+ var GitWebhooksAPI = class {
836
+ constructor(c) {
837
+ this.c = c;
838
+ }
839
+ list(repo) {
840
+ return this.c.get(`${repoPath(repo)}/hooks`);
841
+ }
842
+ /** GitHub-style payloads, signed with `X-Hub-Signature-256` when there's a secret. */
843
+ create(repo, opts) {
844
+ return this.c.post(`${repoPath(repo)}/hooks`, opts);
845
+ }
846
+ get(repo, id) {
847
+ return this.c.get(`${repoPath(repo)}/hooks/${id}`);
848
+ }
849
+ update(repo, id, opts) {
850
+ return this.c.patch(`${repoPath(repo)}/hooks/${id}`, opts);
851
+ }
852
+ delete(repo, id) {
853
+ return this.c.delete(`${repoPath(repo)}/hooks/${id}`);
854
+ }
855
+ ping(repo, id) {
856
+ return this.c.post(`${repoPath(repo)}/hooks/${id}/pings`);
857
+ }
858
+ deliveries(repo, id) {
859
+ return this.c.get(`${repoPath(repo)}/hooks/${id}/deliveries`);
860
+ }
861
+ delivery(repo, id, deliveryId) {
862
+ return this.c.get(`${repoPath(repo)}/hooks/${id}/deliveries/${deliveryId}`);
863
+ }
864
+ redeliver(repo, id, deliveryId) {
865
+ return this.c.post(`${repoPath(repo)}/hooks/${id}/deliveries/${deliveryId}/redeliver`);
866
+ }
867
+ };
868
+ var GitBranchRulesAPI = class {
869
+ constructor(c) {
870
+ this.c = c;
871
+ }
872
+ list(repo) {
873
+ return this.c.get(`${repoPath(repo)}/branch-rules`);
874
+ }
875
+ create(repo, opts) {
876
+ return this.c.post(`${repoPath(repo)}/branch-rules`, opts);
877
+ }
878
+ update(repo, id, opts) {
879
+ return this.c.patch(`${repoPath(repo)}/branch-rules/${id}`, opts);
880
+ }
881
+ delete(repo, id) {
882
+ return this.c.delete(`${repoPath(repo)}/branch-rules/${id}`);
883
+ }
884
+ };
885
+ var GitLabelsAPI = class {
886
+ constructor(c) {
887
+ this.c = c;
888
+ }
889
+ list(repo) {
890
+ return this.c.get(`${repoPath(repo)}/labels`);
891
+ }
892
+ create(repo, opts) {
893
+ return this.c.post(`${repoPath(repo)}/labels`, opts);
894
+ }
895
+ update(repo, name, opts) {
896
+ return this.c.patch(`${repoPath(repo)}/labels/${seg(name)}`, opts);
897
+ }
898
+ delete(repo, name) {
899
+ return this.c.delete(`${repoPath(repo)}/labels/${seg(name)}`);
900
+ }
901
+ };
902
+ var GitReleasesAPI = class {
903
+ constructor(c) {
904
+ this.c = c;
905
+ }
906
+ list(repo) {
907
+ return this.c.get(`${repoPath(repo)}/releases`);
908
+ }
909
+ /** Creates the tag too when it doesn't exist (at `target`, default branch by default). */
910
+ create(repo, opts) {
911
+ return this.c.post(`${repoPath(repo)}/releases`, opts);
912
+ }
913
+ get(repo, id) {
914
+ return this.c.get(`${repoPath(repo)}/releases/${id}`);
915
+ }
916
+ getByTag(repo, tag) {
917
+ return this.c.get(`${repoPath(repo)}/releases/tags/${seg(tag)}`);
918
+ }
919
+ update(repo, id, opts) {
920
+ return this.c.patch(`${repoPath(repo)}/releases/${id}`, opts);
921
+ }
922
+ delete(repo, id) {
923
+ return this.c.delete(`${repoPath(repo)}/releases/${id}`);
924
+ }
925
+ async uploadAsset(repo, id, name, data, contentType = "application/octet-stream") {
926
+ const body = typeof data === "string" || data instanceof Blob ? data : new Blob([new Uint8Array(data)]);
927
+ const res = await this.c.raw("POST", qs(`${repoPath(repo)}/releases/${id}/assets`, { name }), body, { "Content-Type": contentType });
928
+ return res.json();
929
+ }
930
+ downloadAsset(repo, id, assetId) {
931
+ return this.c.bytes(`${repoPath(repo)}/releases/${id}/assets/${assetId}`);
932
+ }
933
+ deleteAsset(repo, id, assetId) {
934
+ return this.c.delete(`${repoPath(repo)}/releases/${id}/assets/${assetId}`);
935
+ }
936
+ };
937
+ var GitStatusesAPI = class {
938
+ constructor(c) {
939
+ this.c = c;
940
+ }
941
+ /** Report a check on a commit (what required checks in branch rules wait for). */
942
+ create(repo, sha, status) {
943
+ return this.c.post(`${repoPath(repo)}/statuses/${seg(sha)}`, status);
944
+ }
945
+ list(repo, ref) {
946
+ return this.c.get(`${repoPath(repo)}/commits/${seg(ref)}/statuses`);
947
+ }
948
+ combined(repo, ref) {
949
+ return this.c.get(`${repoPath(repo)}/commits/${seg(ref)}/status`);
950
+ }
951
+ };
952
+ var GitNotificationsAPI = class {
953
+ constructor(c) {
954
+ this.c = c;
955
+ }
956
+ list(opts = {}) {
957
+ return this.c.get(qs("/api/notifications", { all: opts.all ? 1 : void 0 }));
958
+ }
959
+ markRead(ids) {
960
+ return this.c.post("/api/notifications/read", ids?.length ? { ids } : { all: true });
961
+ }
962
+ };
963
+ var GitTokensAPI = class {
964
+ constructor(c) {
965
+ this.c = c;
966
+ }
967
+ list() {
968
+ return this.c.get("/api/user/tokens");
969
+ }
970
+ /** A Lizard Git token (lzg_...), optionally limited to repos, refs, paths, or read-only. The secret is in `token`, once. */
971
+ create(opts) {
972
+ return this.c.post("/api/user/tokens", opts);
973
+ }
974
+ delete(id) {
975
+ return this.c.delete(`/api/user/tokens/${id}`);
976
+ }
977
+ };
978
+ var GitAPI = class {
979
+ constructor(config, url) {
980
+ const c = this.client = new GitClient(config, url);
981
+ this.repos = new GitReposAPI(c);
982
+ this.issues = new GitIssuesOnlyAPI(c);
983
+ this.pulls = new GitPullsAPI(c);
984
+ this.actions = new GitActionsAPI(c);
985
+ this.collaborators = new GitCollaboratorsAPI(c);
986
+ this.webhooks = new GitWebhooksAPI(c);
987
+ this.branchRules = new GitBranchRulesAPI(c);
988
+ this.labels = new GitLabelsAPI(c);
989
+ this.releases = new GitReleasesAPI(c);
990
+ this.statuses = new GitStatusesAPI(c);
991
+ this.notifications = new GitNotificationsAPI(c);
992
+ this.tokens = new GitTokensAPI(c);
993
+ }
994
+ /** Who the key signs in as on Lizard Git. */
995
+ me() {
996
+ return this.client.get("/api/session");
997
+ }
998
+ user(login) {
999
+ return this.client.get(`/api/users/${seg(login)}`);
1000
+ }
1001
+ };
1002
+
484
1003
  // src/project.ts
485
1004
  import { createHash } from "crypto";
486
1005
  var projectIdCache = /* @__PURE__ */ new Map();
@@ -1360,8 +1879,8 @@ var ProjectsAPI = class {
1360
1879
  }
1361
1880
  /** List all projects the API key can access. */
1362
1881
  list(opts) {
1363
- const qs = opts?.workspaceId ? `?workspaceId=${opts.workspaceId}` : "";
1364
- return this.client.get(`/api/projects${qs}`);
1882
+ const qs2 = opts?.workspaceId ? `?workspaceId=${opts.workspaceId}` : "";
1883
+ return this.client.get(`/api/projects${qs2}`);
1365
1884
  }
1366
1885
  /** Get a project by ID. */
1367
1886
  get(id) {
@@ -1754,8 +2273,8 @@ var WorkspacesAPI = class {
1754
2273
  * volumes and snapshots first.
1755
2274
  */
1756
2275
  delete(id, opts) {
1757
- const qs = opts?.force ? "" : "?requireEmpty=true";
1758
- return this.client.delete(`/api/workspaces/${id}${qs}`);
2276
+ const qs2 = opts?.force ? "" : "?requireEmpty=true";
2277
+ return this.client.delete(`/api/workspaces/${id}${qs2}`);
1759
2278
  }
1760
2279
  /**
1761
2280
  * Find a workspace by name, slug, or id, or `null` if there is no such workspace.
@@ -2037,6 +2556,7 @@ var Lizard = class {
2037
2556
  this.metrics = new MetricsAPI(platform);
2038
2557
  this.storage = new StorageAPI(platform);
2039
2558
  this.github = new GitHubAPI(platform);
2559
+ this.git = new GitAPI(this.config, opts.gitUrl);
2040
2560
  }
2041
2561
  // ── Sandbox convenience methods (backward-compatible) ──────────────────
2042
2562
  /** Resolve the client's project reference to a stable project ID (cached). */
@@ -2648,10 +3168,26 @@ export {
2648
3168
  CodeSandbox,
2649
3169
  ConfigApplyError,
2650
3170
  ConflictError,
3171
+ DEFAULT_GIT_URL,
2651
3172
  DeployHandle,
2652
3173
  Desktop,
2653
3174
  DomainsAPI,
3175
+ GitAPI,
3176
+ GitActionsAPI,
3177
+ GitBranchRulesAPI,
3178
+ GitClient,
3179
+ GitCollaboratorsAPI,
2654
3180
  GitHubAPI,
3181
+ GitIssuesOnlyAPI,
3182
+ GitLabelsAPI,
3183
+ GitNotificationsAPI,
3184
+ GitPullsAPI,
3185
+ GitReleasesAPI,
3186
+ GitReposAPI,
3187
+ GitSecretsAPI,
3188
+ GitStatusesAPI,
3189
+ GitTokensAPI,
3190
+ GitWebhooksAPI,
2655
3191
  LimitExceededError,
2656
3192
  Lizard,
2657
3193
  LizardCLI,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lizard-build/sdk",
3
- "version": "0.1.72",
3
+ "version": "0.1.73",
4
4
  "description": "TypeScript SDK for Lizard: Firecracker microVM sandboxes, code execution and cloud apps.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",