@lizard-build/sdk 0.1.71 → 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();
@@ -812,7 +1331,7 @@ var Fs = class {
812
1331
  * }, 1000)
813
1332
  * ```
814
1333
  *
815
- * Not available on Firecracker sandboxes yet: the call throws `LizardError` (501).
1334
+ * Not available yet: the call throws `LizardError` (501).
816
1335
  */
817
1336
  async watch(path, opts) {
818
1337
  const res = await fetch(`${this.config.apiUrl}/api/sandboxes/${this.sandboxId}/files/watch`, {
@@ -1277,7 +1796,7 @@ var Sandbox = class extends SandboxClient {
1277
1796
  * snapshot. A Firecracker snapshot is `ready` as soon as this returns: about 2 s, up to
1278
1797
  * a few minutes for a sandbox that filled most of its memory (its memory is uploaded
1279
1798
  * first), so the call waits up to 11 minutes. Start copies of it with {@link Sandbox.restore}
1280
- * (about 0.4 s each). The source keeps running. `poolSize` applies to container sandboxes only.
1799
+ * (about 0.4 s each). The source keeps running. Firecracker snapshots ignore `poolSize`.
1281
1800
  */
1282
1801
  snapshot(name = `Snapshot ${this.sandboxId}`, opts = {}) {
1283
1802
  return new PlatformClient(this.resolveOpts()).post(`/api/sandboxes/${this.sandboxId}/snapshot`, { name, poolSize: opts.poolSize ?? 5, scope: opts.scope }, SNAPSHOT_TIMEOUT_MS);
@@ -1285,7 +1804,7 @@ var Sandbox = class extends SandboxClient {
1285
1804
  unexpose(port) {
1286
1805
  return new PlatformClient(this.resolveOpts()).delete(`/api/sandboxes/${this.sandboxId}/expose/${port}`);
1287
1806
  }
1288
- /** Stream the sandbox's logs. Not available on Firecracker sandboxes yet (`LizardError`, 501). */
1807
+ /** Stream the sandbox's logs. Not available yet (`LizardError`, 501). */
1289
1808
  logs(opts = {}) {
1290
1809
  return new PlatformClient(this.resolveOpts()).events(query(`/api/sandboxes/${this.sandboxId}/logs`, { tail: opts.tail }), { signal: opts.signal });
1291
1810
  }
@@ -1324,7 +1843,7 @@ var Sandbox = class extends SandboxClient {
1324
1843
  static setSnapshotWarmPool(snapshotId, poolSize, opts) {
1325
1844
  return new PlatformClient(opts ?? {}).patch(`/api/sandbox-snapshots/${encodeURIComponent(snapshotId)}`, { poolSize });
1326
1845
  }
1327
- /** Release idle warm copies (container snapshots); retain saved state and existing claimed sandboxes. */
1846
+ /** Release idle warm copies of an older snapshot; retain saved state and existing claimed sandboxes. Firecracker snapshots have no warm copies. */
1328
1847
  static pauseSnapshot(snapshotId, opts) {
1329
1848
  return new PlatformClient(opts ?? {}).post(`/api/sandbox-snapshots/${encodeURIComponent(snapshotId)}/pause`, {});
1330
1849
  }
@@ -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.
@@ -1945,13 +2464,11 @@ var Volume = class _Volume {
1945
2464
  /**
1946
2465
  * Resize a volume, by name or by id, to `sizeGb`.
1947
2466
  *
1948
- * A Firecracker volume (the default) can only grow. The change is in place and
1949
- * online: a sandbox with the volume attached keeps running and sees the new size.
1950
- * A smaller size throws `LizardError` (400) with `code: 'volume_shrink_unsupported'`.
2467
+ * A volume grows in place and online: a sandbox with the volume attached keeps
2468
+ * running and sees the new size. A smaller size throws `LizardError` (400) with
2469
+ * `code: 'volume_shrink_unsupported'`.
1951
2470
  *
1952
- * On a container volume the change is in place and online: the size is a quota,
1953
- * so no data is copied and a mounted sandbox sees the new size immediately. Both
1954
- * growing and shrinking are allowed, but a shrink must leave at least 10% of the
2471
+ * An older volume can also shrink, but the shrink must leave at least 10% of the
1955
2472
  * new size free — otherwise it throws `ConflictError` with
1956
2473
  * `code: 'volume_too_full_to_shrink'`.
1957
2474
  *
@@ -1961,7 +2478,7 @@ var Volume = class _Volume {
1961
2478
  * `volume_capacity_unavailable`, and `volume_resize_timeout` (a `TimeoutError`;
1962
2479
  * the size is left unchanged).
1963
2480
  *
1964
- * @returns The volume's updated info, including `sizeEnforced` for a container volume.
2481
+ * @returns The volume's updated info, including `sizeEnforced` for an older volume.
1965
2482
  */
1966
2483
  static async resize(projectId, nameOrId, sizeGb, opts) {
1967
2484
  const config = new ConnectionConfig(opts);
@@ -2039,6 +2556,7 @@ var Lizard = class {
2039
2556
  this.metrics = new MetricsAPI(platform);
2040
2557
  this.storage = new StorageAPI(platform);
2041
2558
  this.github = new GitHubAPI(platform);
2559
+ this.git = new GitAPI(this.config, opts.gitUrl);
2042
2560
  }
2043
2561
  // ── Sandbox convenience methods (backward-compatible) ──────────────────
2044
2562
  /** Resolve the client's project reference to a stable project ID (cached). */
@@ -2650,10 +3168,26 @@ export {
2650
3168
  CodeSandbox,
2651
3169
  ConfigApplyError,
2652
3170
  ConflictError,
3171
+ DEFAULT_GIT_URL,
2653
3172
  DeployHandle,
2654
3173
  Desktop,
2655
3174
  DomainsAPI,
3175
+ GitAPI,
3176
+ GitActionsAPI,
3177
+ GitBranchRulesAPI,
3178
+ GitClient,
3179
+ GitCollaboratorsAPI,
2656
3180
  GitHubAPI,
3181
+ GitIssuesOnlyAPI,
3182
+ GitLabelsAPI,
3183
+ GitNotificationsAPI,
3184
+ GitPullsAPI,
3185
+ GitReleasesAPI,
3186
+ GitReposAPI,
3187
+ GitSecretsAPI,
3188
+ GitStatusesAPI,
3189
+ GitTokensAPI,
3190
+ GitWebhooksAPI,
2657
3191
  LimitExceededError,
2658
3192
  Lizard,
2659
3193
  LizardCLI,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lizard-build/sdk",
3
- "version": "0.1.71",
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",