@lizard-build/sdk 0.1.22 → 0.1.24
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/README.md +9 -7
- package/dist/index.d.mts +417 -30
- package/dist/index.d.ts +417 -30
- package/dist/index.js +436 -16
- package/dist/index.mjs +435 -16
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
AuthenticationError: () => AuthenticationError,
|
|
24
24
|
CodeSandbox: () => CodeSandbox,
|
|
25
|
+
DeployHandle: () => DeployHandle,
|
|
25
26
|
Lizard: () => Lizard,
|
|
26
27
|
LizardError: () => LizardError,
|
|
27
28
|
NotFoundError: () => NotFoundError,
|
|
@@ -369,7 +370,7 @@ var Sandbox = class extends SandboxClient {
|
|
|
369
370
|
* Connect to an existing sandbox by its ID.
|
|
370
371
|
*
|
|
371
372
|
* If the sandbox is currently paused, it will be automatically resumed
|
|
372
|
-
*
|
|
373
|
+
* before this call returns.
|
|
373
374
|
*
|
|
374
375
|
* @example
|
|
375
376
|
* ```ts
|
|
@@ -400,10 +401,15 @@ var Sandbox = class extends SandboxClient {
|
|
|
400
401
|
return SandboxClient.killSandbox(this.sandboxId, this.resolveOpts(opts));
|
|
401
402
|
}
|
|
402
403
|
/**
|
|
403
|
-
*
|
|
404
|
+
* Pause the sandbox microVM by freezing its vCPUs.
|
|
404
405
|
*
|
|
405
|
-
*
|
|
406
|
-
* to
|
|
406
|
+
* Memory, filesystem, and running processes are held in the host's RAM —
|
|
407
|
+
* nothing is written to disk, so a paused sandbox does not survive a host
|
|
408
|
+
* failure. Resume with `sandbox.resume()` or `Sandbox.connect(id)`.
|
|
409
|
+
*
|
|
410
|
+
* Pausing does not stop the timeout: a paused sandbox is still deleted at
|
|
411
|
+
* its original expiry (default 5 minutes). Pass `timeoutMs: 0` at create
|
|
412
|
+
* time to opt out of expiry.
|
|
407
413
|
*
|
|
408
414
|
* @returns `true` if paused successfully.
|
|
409
415
|
*/
|
|
@@ -411,7 +417,7 @@ var Sandbox = class extends SandboxClient {
|
|
|
411
417
|
return SandboxClient.pauseSandbox(this.sandboxId, this.resolveOpts(opts));
|
|
412
418
|
}
|
|
413
419
|
/**
|
|
414
|
-
* Resume a paused sandbox
|
|
420
|
+
* Resume a paused sandbox by unfreezing its vCPUs.
|
|
415
421
|
*
|
|
416
422
|
* @returns `true` if resumed successfully.
|
|
417
423
|
*/
|
|
@@ -456,19 +462,437 @@ var Sandbox = class extends SandboxClient {
|
|
|
456
462
|
Sandbox.defaultTemplate = "base";
|
|
457
463
|
Sandbox.defaultTimeoutMs = DEFAULT_SANDBOX_TIMEOUT_MS;
|
|
458
464
|
|
|
465
|
+
// src/platform/client.ts
|
|
466
|
+
var PlatformClient = class {
|
|
467
|
+
constructor(opts) {
|
|
468
|
+
this.config = new ConnectionConfig(opts);
|
|
469
|
+
}
|
|
470
|
+
async get(path) {
|
|
471
|
+
const res = await fetch(`${this.config.apiUrl}${path}`, {
|
|
472
|
+
headers: this.config.headers
|
|
473
|
+
});
|
|
474
|
+
if (!res.ok) await handleApiError(res);
|
|
475
|
+
return res.json();
|
|
476
|
+
}
|
|
477
|
+
async post(path, body) {
|
|
478
|
+
const res = await fetch(`${this.config.apiUrl}${path}`, {
|
|
479
|
+
method: "POST",
|
|
480
|
+
headers: this.config.headers,
|
|
481
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
482
|
+
});
|
|
483
|
+
if (!res.ok) await handleApiError(res);
|
|
484
|
+
return res.json();
|
|
485
|
+
}
|
|
486
|
+
async patch(path, body) {
|
|
487
|
+
const res = await fetch(`${this.config.apiUrl}${path}`, {
|
|
488
|
+
method: "PATCH",
|
|
489
|
+
headers: this.config.headers,
|
|
490
|
+
body: JSON.stringify(body)
|
|
491
|
+
});
|
|
492
|
+
if (!res.ok) await handleApiError(res);
|
|
493
|
+
return res.json();
|
|
494
|
+
}
|
|
495
|
+
async delete(path, body) {
|
|
496
|
+
const res = await fetch(`${this.config.apiUrl}${path}`, {
|
|
497
|
+
method: "DELETE",
|
|
498
|
+
headers: this.config.headers,
|
|
499
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
500
|
+
});
|
|
501
|
+
if (!res.ok) await handleApiError(res);
|
|
502
|
+
if (res.status === 204 || res.headers.get("content-length") === "0") return void 0;
|
|
503
|
+
return res.json();
|
|
504
|
+
}
|
|
505
|
+
async postForm(path, form) {
|
|
506
|
+
const headers = { "X-API-Key": this.config.apiKey };
|
|
507
|
+
const res = await fetch(`${this.config.apiUrl}${path}`, {
|
|
508
|
+
method: "POST",
|
|
509
|
+
headers,
|
|
510
|
+
body: form
|
|
511
|
+
});
|
|
512
|
+
if (!res.ok) await handleApiError(res);
|
|
513
|
+
return res.json();
|
|
514
|
+
}
|
|
515
|
+
/** Stream SSE events from a path, calling handler for each data line. */
|
|
516
|
+
async *streamSse(path) {
|
|
517
|
+
const res = await fetch(`${this.config.apiUrl}${path}`, {
|
|
518
|
+
headers: this.config.headers
|
|
519
|
+
});
|
|
520
|
+
if (!res.ok) await handleApiError(res);
|
|
521
|
+
if (!res.body) return;
|
|
522
|
+
const reader = res.body.getReader();
|
|
523
|
+
const decoder = new TextDecoder();
|
|
524
|
+
let buf = "";
|
|
525
|
+
while (true) {
|
|
526
|
+
const { done, value } = await reader.read();
|
|
527
|
+
if (done) break;
|
|
528
|
+
buf += decoder.decode(value, { stream: true });
|
|
529
|
+
const events = buf.split("\n\n");
|
|
530
|
+
buf = events.pop();
|
|
531
|
+
for (const evt of events) {
|
|
532
|
+
const dataLine = evt.split("\n").find((l) => l.startsWith("data: "));
|
|
533
|
+
if (dataLine) yield dataLine.slice(6);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
// src/platform/projects.ts
|
|
540
|
+
var ProjectsAPI = class {
|
|
541
|
+
constructor(client) {
|
|
542
|
+
this.client = client;
|
|
543
|
+
}
|
|
544
|
+
/** List all projects the API key can access. */
|
|
545
|
+
list(opts) {
|
|
546
|
+
const qs = opts?.workspaceId ? `?workspaceId=${opts.workspaceId}` : "";
|
|
547
|
+
return this.client.get(`/api/projects${qs}`);
|
|
548
|
+
}
|
|
549
|
+
/** Get a project by ID. */
|
|
550
|
+
get(id) {
|
|
551
|
+
return this.client.get(`/api/projects/${id}`);
|
|
552
|
+
}
|
|
553
|
+
/** Create a new project. */
|
|
554
|
+
create(opts) {
|
|
555
|
+
return this.client.post("/api/projects", opts);
|
|
556
|
+
}
|
|
557
|
+
/** Update a project's name. */
|
|
558
|
+
update(id, opts) {
|
|
559
|
+
return this.client.patch(`/api/projects/${id}`, opts);
|
|
560
|
+
}
|
|
561
|
+
/** Delete a project. */
|
|
562
|
+
delete(id) {
|
|
563
|
+
return this.client.delete(`/api/projects/${id}`);
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// src/platform/services.ts
|
|
568
|
+
var DeployHandle = class {
|
|
569
|
+
constructor(client, serviceId, buildId) {
|
|
570
|
+
this._client = client;
|
|
571
|
+
this._serviceId = serviceId;
|
|
572
|
+
this._buildId = buildId;
|
|
573
|
+
}
|
|
574
|
+
get serviceId() {
|
|
575
|
+
return this._serviceId;
|
|
576
|
+
}
|
|
577
|
+
/** Stream build + runtime log lines until the deploy finishes. */
|
|
578
|
+
async *logs() {
|
|
579
|
+
const path = `/api/apps/${this._serviceId}/logs?limit=1000`;
|
|
580
|
+
const res = await fetch(`${this._client.config.apiUrl}${path}`, {
|
|
581
|
+
headers: this._client.config.headers
|
|
582
|
+
});
|
|
583
|
+
if (!res.ok) return;
|
|
584
|
+
const body = await res.json();
|
|
585
|
+
const lines = Array.isArray(body) ? body : body.logs ?? [];
|
|
586
|
+
for (const l of lines) yield typeof l === "string" ? l : JSON.stringify(l);
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Poll until the deploy reaches a terminal state.
|
|
590
|
+
* @returns `{ url, status }` on success; throws `LizardError` on failure.
|
|
591
|
+
*/
|
|
592
|
+
async wait(opts) {
|
|
593
|
+
const deadline = Date.now() + (opts?.timeoutMs ?? 10 * 6e4);
|
|
594
|
+
const pollMs = opts?.pollMs ?? 3e3;
|
|
595
|
+
while (Date.now() < deadline) {
|
|
596
|
+
const svc = await this._client.get(`/api/apps/${this._serviceId}`);
|
|
597
|
+
if (svc.deployStatus === "idle") {
|
|
598
|
+
if (svc.status === "running") return { url: svc.domain ? `https://${svc.domain}` : null, status: "running" };
|
|
599
|
+
if (svc.status === "crashed") throw new LizardError(`Service crashed after deploy`);
|
|
600
|
+
}
|
|
601
|
+
if (svc.deployStatus === "failed") throw new LizardError(`Deploy failed`);
|
|
602
|
+
await new Promise((r) => setTimeout(r, pollMs));
|
|
603
|
+
}
|
|
604
|
+
throw new TimeoutError(`Deploy did not complete within ${opts?.timeoutMs ?? 6e5}ms`);
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
var ServicesAPI = class {
|
|
608
|
+
constructor(client) {
|
|
609
|
+
this.client = client;
|
|
610
|
+
}
|
|
611
|
+
/** List all services in a project. */
|
|
612
|
+
list(opts) {
|
|
613
|
+
return this.client.get(`/api/projects/${opts.projectId}/apps`);
|
|
614
|
+
}
|
|
615
|
+
/** Get a service by ID. */
|
|
616
|
+
get(id) {
|
|
617
|
+
return this.client.get(`/api/apps/${id}`);
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Create a service and start a deploy from a git repo.
|
|
621
|
+
* Returns a DeployHandle to track progress.
|
|
622
|
+
*/
|
|
623
|
+
async deploy(opts) {
|
|
624
|
+
const body = {
|
|
625
|
+
name: opts.name,
|
|
626
|
+
sourceType: opts.sourceType ?? "github",
|
|
627
|
+
repoUrl: opts.repoUrl,
|
|
628
|
+
branch: opts.branch ?? "main",
|
|
629
|
+
startCommand: opts.startCommand,
|
|
630
|
+
buildCommand: opts.buildCommand,
|
|
631
|
+
containerPort: opts.port ?? opts.containerPort,
|
|
632
|
+
region: opts.region
|
|
633
|
+
};
|
|
634
|
+
const svc = await this.client.post(`/api/projects/${opts.projectId}/apps`, body);
|
|
635
|
+
return new DeployHandle(this.client, svc.id);
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Upload a tarball and deploy it.
|
|
639
|
+
*
|
|
640
|
+
* @param opts.source - A `Buffer`, `Blob`, or `Uint8Array` of a `.tar.gz` file.
|
|
641
|
+
*/
|
|
642
|
+
async upload(opts) {
|
|
643
|
+
const form = new FormData();
|
|
644
|
+
const blob = opts.source instanceof Blob ? opts.source : new Blob([opts.source], { type: "application/gzip" });
|
|
645
|
+
form.append("file", blob, "source.tar.gz");
|
|
646
|
+
form.append("name", opts.name);
|
|
647
|
+
if (opts.startCommand) form.append("startCommand", opts.startCommand);
|
|
648
|
+
if (opts.buildCommand) form.append("buildCommand", opts.buildCommand);
|
|
649
|
+
if (opts.port !== void 0) form.append("containerPort", String(opts.port));
|
|
650
|
+
if (opts.region) form.append("region", opts.region);
|
|
651
|
+
const result = await this.client.postForm(
|
|
652
|
+
`/api/projects/${opts.projectId}/apps/upload`,
|
|
653
|
+
form
|
|
654
|
+
);
|
|
655
|
+
return new DeployHandle(this.client, result.id, result.buildId);
|
|
656
|
+
}
|
|
657
|
+
/** Trigger a redeploy (rebuild from current source). */
|
|
658
|
+
async redeploy(id) {
|
|
659
|
+
await this.client.post(`/api/apps/${id}/redeploy`, {});
|
|
660
|
+
return new DeployHandle(this.client, id);
|
|
661
|
+
}
|
|
662
|
+
/** Restart a service without rebuilding. */
|
|
663
|
+
restart(id) {
|
|
664
|
+
return this.client.post(`/api/apps/${id}/restart`, {});
|
|
665
|
+
}
|
|
666
|
+
/** Scale a service. */
|
|
667
|
+
scale(id, opts) {
|
|
668
|
+
return this.client.post(`/api/apps/${id}/scale`, opts);
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Get recent log lines. For a live tail, use the WebSocket API instead.
|
|
672
|
+
*
|
|
673
|
+
* @param opts.limit - Max lines to return (default 200, max 1000).
|
|
674
|
+
*/
|
|
675
|
+
async logs(id, opts) {
|
|
676
|
+
const qs = new URLSearchParams();
|
|
677
|
+
if (opts?.limit) qs.set("limit", String(opts.limit));
|
|
678
|
+
if (opts?.since) qs.set("since", opts.since);
|
|
679
|
+
const result = await this.client.get(`/api/apps/${id}/logs?${qs}`);
|
|
680
|
+
return Array.isArray(result) ? result : result.logs ?? [];
|
|
681
|
+
}
|
|
682
|
+
/** Execute a command inside the running service VM. */
|
|
683
|
+
exec(id, cmd, opts) {
|
|
684
|
+
return this.client.post(`/api/apps/${id}/exec`, { cmd, timeoutMs: opts?.timeoutMs });
|
|
685
|
+
}
|
|
686
|
+
/** Update service configuration. */
|
|
687
|
+
update(id, opts) {
|
|
688
|
+
return this.client.patch(`/api/apps/${id}`, opts);
|
|
689
|
+
}
|
|
690
|
+
/** Delete a service. */
|
|
691
|
+
delete(id) {
|
|
692
|
+
return this.client.delete(`/api/apps/${id}`);
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
// src/platform/addons.ts
|
|
697
|
+
var AddonsAPI = class {
|
|
698
|
+
constructor(client) {
|
|
699
|
+
this.client = client;
|
|
700
|
+
}
|
|
701
|
+
/** List all addons in a project. */
|
|
702
|
+
list(opts) {
|
|
703
|
+
return this.client.get(`/api/projects/${opts.projectId}/addons`);
|
|
704
|
+
}
|
|
705
|
+
/** Get an addon by ID. */
|
|
706
|
+
get(projectId, addonId) {
|
|
707
|
+
return this.client.get(`/api/projects/${projectId}/addons/${addonId}`);
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Create a new managed addon (database, cache, or object store).
|
|
711
|
+
*
|
|
712
|
+
* @example
|
|
713
|
+
* ```ts
|
|
714
|
+
* const pg = await lizard.addons.create({ projectId, type: 'postgres' })
|
|
715
|
+
* // Inject into a service:
|
|
716
|
+
* await lizard.secrets.set({ serviceId: svcId, key: 'DATABASE_URL', value: `${{${pg.name}.DATABASE_URL}}` })
|
|
717
|
+
* ```
|
|
718
|
+
*/
|
|
719
|
+
create(opts) {
|
|
720
|
+
return this.client.post(`/api/projects/${opts.projectId}/addons`, {
|
|
721
|
+
type: opts.type,
|
|
722
|
+
name: opts.name,
|
|
723
|
+
version: opts.version,
|
|
724
|
+
vcpu: opts.vcpu,
|
|
725
|
+
memoryMb: opts.memoryMb,
|
|
726
|
+
storageGb: opts.storageGb
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
/** Delete an addon. */
|
|
730
|
+
delete(projectId, addonId) {
|
|
731
|
+
return this.client.delete(`/api/projects/${projectId}/addons/${addonId}`);
|
|
732
|
+
}
|
|
733
|
+
/** Resize an addon (CPU / memory / storage). */
|
|
734
|
+
resize(projectId, addonId, opts) {
|
|
735
|
+
return this.client.post(`/api/projects/${projectId}/addons/${addonId}/resize`, opts);
|
|
736
|
+
}
|
|
737
|
+
/** Restart an addon VM. */
|
|
738
|
+
redeploy(projectId, addonId) {
|
|
739
|
+
return this.client.post(`/api/projects/${projectId}/addons/${addonId}/redeploy`, {});
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
// src/platform/secrets.ts
|
|
744
|
+
var SecretsAPI = class {
|
|
745
|
+
constructor(client) {
|
|
746
|
+
this.client = client;
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* List secrets for a project or service.
|
|
750
|
+
*
|
|
751
|
+
* @param projectId - The project ID.
|
|
752
|
+
* @param opts.serviceId - If given, list service-scoped secrets instead of project secrets.
|
|
753
|
+
*/
|
|
754
|
+
list(projectId, opts) {
|
|
755
|
+
if (opts?.serviceId) {
|
|
756
|
+
return this.client.get(`/api/apps/${opts.serviceId}/secrets`);
|
|
757
|
+
}
|
|
758
|
+
return this.client.get(`/api/projects/${projectId}/secrets`);
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Set one or more secrets.
|
|
762
|
+
*
|
|
763
|
+
* ```ts
|
|
764
|
+
* // Service-scoped secret (default):
|
|
765
|
+
* await lizard.secrets.set(projectId, { key: 'DATABASE_URL', value: '${{postgres.DATABASE_URL}}', serviceId })
|
|
766
|
+
*
|
|
767
|
+
* // Project-scoped (shared across all services):
|
|
768
|
+
* await lizard.secrets.set(projectId, { key: 'LOG_LEVEL', value: 'info', global: true })
|
|
769
|
+
* ```
|
|
770
|
+
*/
|
|
771
|
+
async set(projectId, secrets) {
|
|
772
|
+
const items = Array.isArray(secrets) ? secrets : [secrets];
|
|
773
|
+
const byService = /* @__PURE__ */ new Map();
|
|
774
|
+
const projectScoped = {};
|
|
775
|
+
for (const s of items) {
|
|
776
|
+
if (s.global || !s.serviceId) {
|
|
777
|
+
projectScoped[s.key] = s.value;
|
|
778
|
+
} else {
|
|
779
|
+
if (!byService.has(s.serviceId)) byService.set(s.serviceId, {});
|
|
780
|
+
byService.get(s.serviceId)[s.key] = s.value;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
const tasks = [];
|
|
784
|
+
if (Object.keys(projectScoped).length > 0) {
|
|
785
|
+
tasks.push(this.client.post(`/api/projects/${projectId}/secrets`, projectScoped));
|
|
786
|
+
}
|
|
787
|
+
for (const [serviceId, kv] of byService) {
|
|
788
|
+
tasks.push(this.client.post(`/api/apps/${serviceId}/secrets`, kv));
|
|
789
|
+
}
|
|
790
|
+
await Promise.all(tasks);
|
|
791
|
+
}
|
|
792
|
+
/** Delete a secret by key. */
|
|
793
|
+
async delete(projectId, opts) {
|
|
794
|
+
if (opts.serviceId) {
|
|
795
|
+
await this.client.delete(`/api/apps/${opts.serviceId}/secrets`, { key: opts.key });
|
|
796
|
+
} else {
|
|
797
|
+
await this.client.delete(`/api/projects/${projectId}/secrets`, { key: opts.key });
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
// src/platform/domains.ts
|
|
803
|
+
var DomainsAPI = class {
|
|
804
|
+
constructor(client) {
|
|
805
|
+
this.client = client;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Show the current domain(s) for a service.
|
|
809
|
+
* The platform-assigned `.onlizard.com` subdomain is always present.
|
|
810
|
+
*/
|
|
811
|
+
list(serviceId) {
|
|
812
|
+
return this.client.get(`/api/apps/${serviceId}/domains`);
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Attach a custom domain to a service.
|
|
816
|
+
* Returns the DNS records to add before calling `verify()`.
|
|
817
|
+
*
|
|
818
|
+
* @example
|
|
819
|
+
* ```ts
|
|
820
|
+
* const info = await lizard.domains.add(serviceId, 'api.example.com')
|
|
821
|
+
* console.log('Add CNAME:', info.cnameTarget)
|
|
822
|
+
* ```
|
|
823
|
+
*/
|
|
824
|
+
add(serviceId, domain) {
|
|
825
|
+
return this.client.post(`/api/apps/${serviceId}/domains`, { domain });
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Verify that DNS records have propagated and activate the domain.
|
|
829
|
+
* Call after adding the CNAME or TXT record returned by `add()`.
|
|
830
|
+
*/
|
|
831
|
+
verify(serviceId, domain) {
|
|
832
|
+
return this.client.post(`/api/apps/${serviceId}/domains/verify`, { domain });
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
// src/platform/metrics.ts
|
|
837
|
+
var MetricsAPI = class {
|
|
838
|
+
constructor(client) {
|
|
839
|
+
this.client = client;
|
|
840
|
+
}
|
|
841
|
+
/** Get CPU metrics for a service or addon. */
|
|
842
|
+
cpu(id, range = "1h") {
|
|
843
|
+
return this.client.get(`/api/apps/${id}/metrics/cpu?range=${range}`);
|
|
844
|
+
}
|
|
845
|
+
/** Get memory metrics for a service or addon. */
|
|
846
|
+
memory(id, range = "1h") {
|
|
847
|
+
return this.client.get(`/api/apps/${id}/metrics/memory?range=${range}`);
|
|
848
|
+
}
|
|
849
|
+
/** Get network I/O metrics for a service. */
|
|
850
|
+
network(id, range = "1h") {
|
|
851
|
+
return this.client.get(`/api/apps/${id}/metrics/network?range=${range}`);
|
|
852
|
+
}
|
|
853
|
+
/** Get disk I/O metrics for a service. */
|
|
854
|
+
disk(id, range = "1h") {
|
|
855
|
+
return this.client.get(`/api/apps/${id}/metrics/disk?range=${range}`);
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Get cost breakdown for a project over the given range.
|
|
859
|
+
* Costs are in USD cents unless the `currency` field says otherwise.
|
|
860
|
+
*/
|
|
861
|
+
cost(projectId, range = "30d") {
|
|
862
|
+
return this.client.get(`/api/projects/${projectId}/metrics/cost?range=${range}`);
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Convenience: fetch all metric types for a service in one call.
|
|
866
|
+
*/
|
|
867
|
+
async all(id, range = "1h") {
|
|
868
|
+
const [cpu, memory, network, disk] = await Promise.all([
|
|
869
|
+
this.cpu(id, range),
|
|
870
|
+
this.memory(id, range),
|
|
871
|
+
this.network(id, range),
|
|
872
|
+
this.disk(id, range)
|
|
873
|
+
]);
|
|
874
|
+
return { cpu, memory, networkRx: network.rx, networkTx: network.tx, diskRead: disk.read, diskWrite: disk.write };
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
|
|
459
878
|
// src/lizard.ts
|
|
460
879
|
var Lizard = class {
|
|
461
880
|
constructor(opts) {
|
|
462
|
-
if (!opts?.project) {
|
|
463
|
-
throw new LizardError(
|
|
464
|
-
'A project is required. Pass project (its ID, slug, or name): new Lizard({ project: "my-project" }).'
|
|
465
|
-
);
|
|
466
|
-
}
|
|
467
881
|
this.config = new ConnectionConfig(opts);
|
|
468
882
|
this.projectRef = opts.project;
|
|
883
|
+
const platform = new PlatformClient(opts);
|
|
884
|
+
this._platform = platform;
|
|
885
|
+
this.projects = new ProjectsAPI(platform);
|
|
886
|
+
this.services = new ServicesAPI(platform);
|
|
887
|
+
this.addons = new AddonsAPI(platform);
|
|
888
|
+
this.secrets = new SecretsAPI(platform);
|
|
889
|
+
this.domains = new DomainsAPI(platform);
|
|
890
|
+
this.metrics = new MetricsAPI(platform);
|
|
469
891
|
}
|
|
892
|
+
// ── Sandbox convenience methods (backward-compatible) ──────────────────
|
|
470
893
|
/** Resolve the client's project reference to a stable project ID (cached). */
|
|
471
894
|
async projectId() {
|
|
895
|
+
if (!this.projectRef) throw new LizardError("No project set. Pass project in Lizard({ project }) to use sandbox APIs.");
|
|
472
896
|
return resolveProjectId(this.projectRef, this.config);
|
|
473
897
|
}
|
|
474
898
|
async connectionOpts(extra) {
|
|
@@ -476,12 +900,7 @@ var Lizard = class {
|
|
|
476
900
|
}
|
|
477
901
|
/**
|
|
478
902
|
* Create a new sandbox in this client's project.
|
|
479
|
-
*
|
|
480
|
-
* @example
|
|
481
|
-
* ```ts
|
|
482
|
-
* const sandbox = await lizard.create('base')
|
|
483
|
-
* const sandbox = await lizard.create('code-interpreter-v1', { timeoutMs: 600_000 })
|
|
484
|
-
* ```
|
|
903
|
+
* @requires `project` to be set in the constructor.
|
|
485
904
|
*/
|
|
486
905
|
async create(template, opts) {
|
|
487
906
|
const projectId = await this.projectId();
|
|
@@ -740,6 +1159,7 @@ var Volume = class _Volume {
|
|
|
740
1159
|
0 && (module.exports = {
|
|
741
1160
|
AuthenticationError,
|
|
742
1161
|
CodeSandbox,
|
|
1162
|
+
DeployHandle,
|
|
743
1163
|
Lizard,
|
|
744
1164
|
LizardError,
|
|
745
1165
|
NotFoundError,
|