@lizard-build/sdk 0.1.29 → 0.1.30
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.d.mts +63 -7
- package/dist/index.d.ts +63 -7
- package/dist/index.js +66 -9
- package/dist/index.mjs +65 -9
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -150,7 +150,14 @@ interface SandboxOpts extends ConnectionOpts {
|
|
|
150
150
|
metadata?: Record<string, string>;
|
|
151
151
|
envs?: Record<string, string>;
|
|
152
152
|
timeoutMs?: number;
|
|
153
|
+
/** Attach a persistent volume by id, mounted at `/data`. */
|
|
153
154
|
volumeId?: string;
|
|
155
|
+
/**
|
|
156
|
+
* Attach a persistent volume by name, mounted at `/data`. A volume's name is its
|
|
157
|
+
* key inside a project, so this is usually what you want — see {@link Volume}.
|
|
158
|
+
* Requires the sandbox's project to be given as an exact `projectId`.
|
|
159
|
+
*/
|
|
160
|
+
volumeName?: string;
|
|
154
161
|
}
|
|
155
162
|
/**
|
|
156
163
|
* Low-level HTTP client for the Lizard sandbox API.
|
|
@@ -293,9 +300,16 @@ declare class Sandbox extends SandboxClient {
|
|
|
293
300
|
* nothing is written to disk, so a paused sandbox does not survive a host
|
|
294
301
|
* failure. Resume with `sandbox.resume()` or `Sandbox.connect(id)`.
|
|
295
302
|
*
|
|
296
|
-
* Pausing does not
|
|
297
|
-
*
|
|
298
|
-
*
|
|
303
|
+
* Pausing does not buy you time past the original deadline. Resuming pushes the
|
|
304
|
+
* expiry forward by however long the sandbox was paused — so a pause/resume cycle
|
|
305
|
+
* costs no runtime — but that adjustment only happens on `resume()`. A sandbox left
|
|
306
|
+
* paused past its original `expiresAt` is deleted there, within a minute, and
|
|
307
|
+
* `resume()` then returns `false`.
|
|
308
|
+
*
|
|
309
|
+
* In other words: pause and come back *before* the original deadline and you lose
|
|
310
|
+
* nothing; leave it paused across the deadline and it is gone. Pass `timeoutMs: 0`
|
|
311
|
+
* at create time to opt out of expiry entirely, which is the only way to park a
|
|
312
|
+
* sandbox indefinitely.
|
|
299
313
|
*
|
|
300
314
|
* @returns `true` if paused successfully.
|
|
301
315
|
*/
|
|
@@ -303,7 +317,12 @@ declare class Sandbox extends SandboxClient {
|
|
|
303
317
|
/**
|
|
304
318
|
* Resume a paused sandbox by unfreezing its vCPUs.
|
|
305
319
|
*
|
|
306
|
-
*
|
|
320
|
+
* The expiry is pushed forward by the time spent paused, so the timeout measures
|
|
321
|
+
* running time rather than wall-clock — provided the original deadline had not
|
|
322
|
+
* already passed while paused. See {@link pause}.
|
|
323
|
+
*
|
|
324
|
+
* @returns `true` if resumed successfully, `false` if the sandbox no longer exists
|
|
325
|
+
* (including because it expired while paused).
|
|
307
326
|
*/
|
|
308
327
|
resume(opts?: ConnectionOpts): Promise<boolean>;
|
|
309
328
|
/**
|
|
@@ -780,6 +799,14 @@ declare class AuthenticationError extends LizardError {
|
|
|
780
799
|
declare class NotFoundError extends LizardError {
|
|
781
800
|
constructor(message?: string);
|
|
782
801
|
}
|
|
802
|
+
/**
|
|
803
|
+
* The resource already exists — most often a volume whose name is already taken in
|
|
804
|
+
* the project. Volume names are the key inside a project, so `Volume.create` refuses
|
|
805
|
+
* to make a second one; catch this, or call `Volume.getOrCreate` instead.
|
|
806
|
+
*/
|
|
807
|
+
declare class ConflictError extends LizardError {
|
|
808
|
+
constructor(message?: string);
|
|
809
|
+
}
|
|
783
810
|
declare class TimeoutError extends LizardError {
|
|
784
811
|
constructor(message?: string);
|
|
785
812
|
}
|
|
@@ -950,19 +977,48 @@ interface CreateVolumeOpts extends ConnectionOpts {
|
|
|
950
977
|
}
|
|
951
978
|
/**
|
|
952
979
|
* A persistent volume that outlives sandboxes.
|
|
953
|
-
*
|
|
980
|
+
*
|
|
981
|
+
* A volume's **name** is its key inside a project: names are unique per project, and
|
|
982
|
+
* every method here accepts either a name or the generated id wherever a volume is
|
|
983
|
+
* addressed. Prefer the name — it is the thing you chose and can reconstruct, while
|
|
984
|
+
* the id only exists after the first create.
|
|
985
|
+
*
|
|
986
|
+
* Names are slugs: lowercase letters, digits and dashes, starting and ending with a
|
|
987
|
+
* letter or digit, up to 64 characters.
|
|
988
|
+
*
|
|
989
|
+
* Mount one to a sandbox via `Sandbox.create({ volumeName: 'my-data' })`.
|
|
954
990
|
*/
|
|
955
991
|
declare class Volume {
|
|
992
|
+
/** The volume's generated id. Stable, but you rarely need it — address by name. */
|
|
956
993
|
readonly volumeId: string;
|
|
994
|
+
/** The volume's name: unique within its project, and usable anywhere `volumeId` is. */
|
|
995
|
+
readonly name?: string;
|
|
957
996
|
private readonly config;
|
|
958
997
|
constructor(opts: {
|
|
959
998
|
volumeId: string;
|
|
999
|
+
name?: string;
|
|
960
1000
|
} & ConnectionOpts);
|
|
1001
|
+
/**
|
|
1002
|
+
* Create a volume. Throws `ConflictError` (409) if the project already has one with
|
|
1003
|
+
* this name — use {@link getOrCreate} when you want "make sure this exists" instead.
|
|
1004
|
+
*/
|
|
961
1005
|
static create(projectId: string, name: string, opts?: CreateVolumeOpts): Promise<Volume>;
|
|
962
|
-
|
|
1006
|
+
/**
|
|
1007
|
+
* Return the project's volume with this name, creating it first if it does not
|
|
1008
|
+
* exist yet. This is the reason a volume's name is its key: an agent that wants
|
|
1009
|
+
* "the scratch disk for this task" no longer has to store an id between runs.
|
|
1010
|
+
*
|
|
1011
|
+
* An existing volume is returned as-is — `sizeGb` applies only to a fresh create
|
|
1012
|
+
* and never resizes one that is already there.
|
|
1013
|
+
*/
|
|
1014
|
+
static getOrCreate(projectId: string, name: string, opts?: CreateVolumeOpts): Promise<Volume>;
|
|
1015
|
+
/** Look a volume up by name or by id. */
|
|
1016
|
+
static get(projectId: string, nameOrId: string, opts?: ConnectionOpts): Promise<Volume>;
|
|
963
1017
|
static list(projectId: string, opts?: ConnectionOpts): Promise<VolumeInfo[]>;
|
|
1018
|
+
/** Delete a volume by name or by id, without constructing one first. */
|
|
1019
|
+
static delete(projectId: string, nameOrId: string, opts?: ConnectionOpts): Promise<void>;
|
|
964
1020
|
getInfo(projectId: string): Promise<VolumeInfo>;
|
|
965
1021
|
delete(projectId: string): Promise<void>;
|
|
966
1022
|
}
|
|
967
1023
|
|
|
968
|
-
export { type Addon, type AddonType, AuthenticationError, type CodeContext, CodeSandbox, type ConnectionOpts, type CostMetrics, type CreateAddonOpts, type CreateContextOpts, type CreateProjectOpts, type CreateServiceOpts, type CreateVolumeOpts, type DeployEvent, DeployHandle, type DomainInfo, Execution, ExecutionError, type FileInfo, type FsOpts, Lizard, LizardError, type LizardOpts, type LogLine, type MetricPoint, type MetricRange, NotFoundError, type ProcessOpts, type ProcessResult, type Project, type RunCodeLanguage, type RunCodeOpts, Sandbox, type SandboxInfo, type SandboxOpts, type ScaleOpts, type Secret, type Service, type ServiceMetrics, type SetSecretOpts, TimeoutError, Volume, type VolumeInfo, resolveProjectId };
|
|
1024
|
+
export { type Addon, type AddonType, AuthenticationError, type CodeContext, CodeSandbox, ConflictError, type ConnectionOpts, type CostMetrics, type CreateAddonOpts, type CreateContextOpts, type CreateProjectOpts, type CreateServiceOpts, type CreateVolumeOpts, type DeployEvent, DeployHandle, type DomainInfo, Execution, ExecutionError, type FileInfo, type FsOpts, Lizard, LizardError, type LizardOpts, type LogLine, type MetricPoint, type MetricRange, NotFoundError, type ProcessOpts, type ProcessResult, type Project, type RunCodeLanguage, type RunCodeOpts, Sandbox, type SandboxInfo, type SandboxOpts, type ScaleOpts, type Secret, type Service, type ServiceMetrics, type SetSecretOpts, TimeoutError, Volume, type VolumeInfo, resolveProjectId };
|
package/dist/index.d.ts
CHANGED
|
@@ -150,7 +150,14 @@ interface SandboxOpts extends ConnectionOpts {
|
|
|
150
150
|
metadata?: Record<string, string>;
|
|
151
151
|
envs?: Record<string, string>;
|
|
152
152
|
timeoutMs?: number;
|
|
153
|
+
/** Attach a persistent volume by id, mounted at `/data`. */
|
|
153
154
|
volumeId?: string;
|
|
155
|
+
/**
|
|
156
|
+
* Attach a persistent volume by name, mounted at `/data`. A volume's name is its
|
|
157
|
+
* key inside a project, so this is usually what you want — see {@link Volume}.
|
|
158
|
+
* Requires the sandbox's project to be given as an exact `projectId`.
|
|
159
|
+
*/
|
|
160
|
+
volumeName?: string;
|
|
154
161
|
}
|
|
155
162
|
/**
|
|
156
163
|
* Low-level HTTP client for the Lizard sandbox API.
|
|
@@ -293,9 +300,16 @@ declare class Sandbox extends SandboxClient {
|
|
|
293
300
|
* nothing is written to disk, so a paused sandbox does not survive a host
|
|
294
301
|
* failure. Resume with `sandbox.resume()` or `Sandbox.connect(id)`.
|
|
295
302
|
*
|
|
296
|
-
* Pausing does not
|
|
297
|
-
*
|
|
298
|
-
*
|
|
303
|
+
* Pausing does not buy you time past the original deadline. Resuming pushes the
|
|
304
|
+
* expiry forward by however long the sandbox was paused — so a pause/resume cycle
|
|
305
|
+
* costs no runtime — but that adjustment only happens on `resume()`. A sandbox left
|
|
306
|
+
* paused past its original `expiresAt` is deleted there, within a minute, and
|
|
307
|
+
* `resume()` then returns `false`.
|
|
308
|
+
*
|
|
309
|
+
* In other words: pause and come back *before* the original deadline and you lose
|
|
310
|
+
* nothing; leave it paused across the deadline and it is gone. Pass `timeoutMs: 0`
|
|
311
|
+
* at create time to opt out of expiry entirely, which is the only way to park a
|
|
312
|
+
* sandbox indefinitely.
|
|
299
313
|
*
|
|
300
314
|
* @returns `true` if paused successfully.
|
|
301
315
|
*/
|
|
@@ -303,7 +317,12 @@ declare class Sandbox extends SandboxClient {
|
|
|
303
317
|
/**
|
|
304
318
|
* Resume a paused sandbox by unfreezing its vCPUs.
|
|
305
319
|
*
|
|
306
|
-
*
|
|
320
|
+
* The expiry is pushed forward by the time spent paused, so the timeout measures
|
|
321
|
+
* running time rather than wall-clock — provided the original deadline had not
|
|
322
|
+
* already passed while paused. See {@link pause}.
|
|
323
|
+
*
|
|
324
|
+
* @returns `true` if resumed successfully, `false` if the sandbox no longer exists
|
|
325
|
+
* (including because it expired while paused).
|
|
307
326
|
*/
|
|
308
327
|
resume(opts?: ConnectionOpts): Promise<boolean>;
|
|
309
328
|
/**
|
|
@@ -780,6 +799,14 @@ declare class AuthenticationError extends LizardError {
|
|
|
780
799
|
declare class NotFoundError extends LizardError {
|
|
781
800
|
constructor(message?: string);
|
|
782
801
|
}
|
|
802
|
+
/**
|
|
803
|
+
* The resource already exists — most often a volume whose name is already taken in
|
|
804
|
+
* the project. Volume names are the key inside a project, so `Volume.create` refuses
|
|
805
|
+
* to make a second one; catch this, or call `Volume.getOrCreate` instead.
|
|
806
|
+
*/
|
|
807
|
+
declare class ConflictError extends LizardError {
|
|
808
|
+
constructor(message?: string);
|
|
809
|
+
}
|
|
783
810
|
declare class TimeoutError extends LizardError {
|
|
784
811
|
constructor(message?: string);
|
|
785
812
|
}
|
|
@@ -950,19 +977,48 @@ interface CreateVolumeOpts extends ConnectionOpts {
|
|
|
950
977
|
}
|
|
951
978
|
/**
|
|
952
979
|
* A persistent volume that outlives sandboxes.
|
|
953
|
-
*
|
|
980
|
+
*
|
|
981
|
+
* A volume's **name** is its key inside a project: names are unique per project, and
|
|
982
|
+
* every method here accepts either a name or the generated id wherever a volume is
|
|
983
|
+
* addressed. Prefer the name — it is the thing you chose and can reconstruct, while
|
|
984
|
+
* the id only exists after the first create.
|
|
985
|
+
*
|
|
986
|
+
* Names are slugs: lowercase letters, digits and dashes, starting and ending with a
|
|
987
|
+
* letter or digit, up to 64 characters.
|
|
988
|
+
*
|
|
989
|
+
* Mount one to a sandbox via `Sandbox.create({ volumeName: 'my-data' })`.
|
|
954
990
|
*/
|
|
955
991
|
declare class Volume {
|
|
992
|
+
/** The volume's generated id. Stable, but you rarely need it — address by name. */
|
|
956
993
|
readonly volumeId: string;
|
|
994
|
+
/** The volume's name: unique within its project, and usable anywhere `volumeId` is. */
|
|
995
|
+
readonly name?: string;
|
|
957
996
|
private readonly config;
|
|
958
997
|
constructor(opts: {
|
|
959
998
|
volumeId: string;
|
|
999
|
+
name?: string;
|
|
960
1000
|
} & ConnectionOpts);
|
|
1001
|
+
/**
|
|
1002
|
+
* Create a volume. Throws `ConflictError` (409) if the project already has one with
|
|
1003
|
+
* this name — use {@link getOrCreate} when you want "make sure this exists" instead.
|
|
1004
|
+
*/
|
|
961
1005
|
static create(projectId: string, name: string, opts?: CreateVolumeOpts): Promise<Volume>;
|
|
962
|
-
|
|
1006
|
+
/**
|
|
1007
|
+
* Return the project's volume with this name, creating it first if it does not
|
|
1008
|
+
* exist yet. This is the reason a volume's name is its key: an agent that wants
|
|
1009
|
+
* "the scratch disk for this task" no longer has to store an id between runs.
|
|
1010
|
+
*
|
|
1011
|
+
* An existing volume is returned as-is — `sizeGb` applies only to a fresh create
|
|
1012
|
+
* and never resizes one that is already there.
|
|
1013
|
+
*/
|
|
1014
|
+
static getOrCreate(projectId: string, name: string, opts?: CreateVolumeOpts): Promise<Volume>;
|
|
1015
|
+
/** Look a volume up by name or by id. */
|
|
1016
|
+
static get(projectId: string, nameOrId: string, opts?: ConnectionOpts): Promise<Volume>;
|
|
963
1017
|
static list(projectId: string, opts?: ConnectionOpts): Promise<VolumeInfo[]>;
|
|
1018
|
+
/** Delete a volume by name or by id, without constructing one first. */
|
|
1019
|
+
static delete(projectId: string, nameOrId: string, opts?: ConnectionOpts): Promise<void>;
|
|
964
1020
|
getInfo(projectId: string): Promise<VolumeInfo>;
|
|
965
1021
|
delete(projectId: string): Promise<void>;
|
|
966
1022
|
}
|
|
967
1023
|
|
|
968
|
-
export { type Addon, type AddonType, AuthenticationError, type CodeContext, CodeSandbox, type ConnectionOpts, type CostMetrics, type CreateAddonOpts, type CreateContextOpts, type CreateProjectOpts, type CreateServiceOpts, type CreateVolumeOpts, type DeployEvent, DeployHandle, type DomainInfo, Execution, ExecutionError, type FileInfo, type FsOpts, Lizard, LizardError, type LizardOpts, type LogLine, type MetricPoint, type MetricRange, NotFoundError, type ProcessOpts, type ProcessResult, type Project, type RunCodeLanguage, type RunCodeOpts, Sandbox, type SandboxInfo, type SandboxOpts, type ScaleOpts, type Secret, type Service, type ServiceMetrics, type SetSecretOpts, TimeoutError, Volume, type VolumeInfo, resolveProjectId };
|
|
1024
|
+
export { type Addon, type AddonType, AuthenticationError, type CodeContext, CodeSandbox, ConflictError, type ConnectionOpts, type CostMetrics, type CreateAddonOpts, type CreateContextOpts, type CreateProjectOpts, type CreateServiceOpts, type CreateVolumeOpts, type DeployEvent, DeployHandle, type DomainInfo, Execution, ExecutionError, type FileInfo, type FsOpts, Lizard, LizardError, type LizardOpts, type LogLine, type MetricPoint, type MetricRange, NotFoundError, type ProcessOpts, type ProcessResult, type Project, type RunCodeLanguage, type RunCodeOpts, Sandbox, type SandboxInfo, type SandboxOpts, type ScaleOpts, type Secret, type Service, type ServiceMetrics, type SetSecretOpts, TimeoutError, Volume, type VolumeInfo, resolveProjectId };
|
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
|
+
ConflictError: () => ConflictError,
|
|
25
26
|
DeployHandle: () => DeployHandle,
|
|
26
27
|
Lizard: () => Lizard,
|
|
27
28
|
LizardError: () => LizardError,
|
|
@@ -74,6 +75,12 @@ var NotFoundError = class extends LizardError {
|
|
|
74
75
|
this.name = "NotFoundError";
|
|
75
76
|
}
|
|
76
77
|
};
|
|
78
|
+
var ConflictError = class extends LizardError {
|
|
79
|
+
constructor(message = "Resource already exists") {
|
|
80
|
+
super(message);
|
|
81
|
+
this.name = "ConflictError";
|
|
82
|
+
}
|
|
83
|
+
};
|
|
77
84
|
var TimeoutError = class extends LizardError {
|
|
78
85
|
constructor(message = "Sandbox operation timed out") {
|
|
79
86
|
super(message);
|
|
@@ -90,6 +97,7 @@ async function handleApiError(res) {
|
|
|
90
97
|
}
|
|
91
98
|
if (res.status === 401 || res.status === 403) throw new AuthenticationError(message);
|
|
92
99
|
if (res.status === 404) throw new NotFoundError(message);
|
|
100
|
+
if (res.status === 409) throw new ConflictError(message);
|
|
93
101
|
if (res.status === 408 || res.status === 504) throw new TimeoutError(message);
|
|
94
102
|
throw new LizardError(`API error ${res.status}: ${message}`);
|
|
95
103
|
}
|
|
@@ -278,7 +286,8 @@ var SandboxClient = class {
|
|
|
278
286
|
projectId,
|
|
279
287
|
metadata: opts?.metadata,
|
|
280
288
|
envs: opts?.envs,
|
|
281
|
-
volumeId: opts?.volumeId
|
|
289
|
+
volumeId: opts?.volumeId,
|
|
290
|
+
volumeName: opts?.volumeName
|
|
282
291
|
})
|
|
283
292
|
});
|
|
284
293
|
if (!res.ok) await handleApiError(res);
|
|
@@ -407,9 +416,16 @@ var Sandbox = class extends SandboxClient {
|
|
|
407
416
|
* nothing is written to disk, so a paused sandbox does not survive a host
|
|
408
417
|
* failure. Resume with `sandbox.resume()` or `Sandbox.connect(id)`.
|
|
409
418
|
*
|
|
410
|
-
* Pausing does not
|
|
411
|
-
*
|
|
412
|
-
*
|
|
419
|
+
* Pausing does not buy you time past the original deadline. Resuming pushes the
|
|
420
|
+
* expiry forward by however long the sandbox was paused — so a pause/resume cycle
|
|
421
|
+
* costs no runtime — but that adjustment only happens on `resume()`. A sandbox left
|
|
422
|
+
* paused past its original `expiresAt` is deleted there, within a minute, and
|
|
423
|
+
* `resume()` then returns `false`.
|
|
424
|
+
*
|
|
425
|
+
* In other words: pause and come back *before* the original deadline and you lose
|
|
426
|
+
* nothing; leave it paused across the deadline and it is gone. Pass `timeoutMs: 0`
|
|
427
|
+
* at create time to opt out of expiry entirely, which is the only way to park a
|
|
428
|
+
* sandbox indefinitely.
|
|
413
429
|
*
|
|
414
430
|
* @returns `true` if paused successfully.
|
|
415
431
|
*/
|
|
@@ -419,7 +435,12 @@ var Sandbox = class extends SandboxClient {
|
|
|
419
435
|
/**
|
|
420
436
|
* Resume a paused sandbox by unfreezing its vCPUs.
|
|
421
437
|
*
|
|
422
|
-
*
|
|
438
|
+
* The expiry is pushed forward by the time spent paused, so the timeout measures
|
|
439
|
+
* running time rather than wall-clock — provided the original deadline had not
|
|
440
|
+
* already passed while paused. See {@link pause}.
|
|
441
|
+
*
|
|
442
|
+
* @returns `true` if resumed successfully, `false` if the sandbox no longer exists
|
|
443
|
+
* (including because it expired while paused).
|
|
423
444
|
*/
|
|
424
445
|
async resume(opts) {
|
|
425
446
|
return SandboxClient.resumeSandbox(this.sandboxId, this.resolveOpts(opts));
|
|
@@ -1111,8 +1132,13 @@ async function* readLines(body) {
|
|
|
1111
1132
|
var Volume = class _Volume {
|
|
1112
1133
|
constructor(opts) {
|
|
1113
1134
|
this.volumeId = opts.volumeId;
|
|
1135
|
+
this.name = opts.name;
|
|
1114
1136
|
this.config = new ConnectionConfig(opts);
|
|
1115
1137
|
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Create a volume. Throws `ConflictError` (409) if the project already has one with
|
|
1140
|
+
* this name — use {@link getOrCreate} when you want "make sure this exists" instead.
|
|
1141
|
+
*/
|
|
1116
1142
|
static async create(projectId, name, opts) {
|
|
1117
1143
|
const config = new ConnectionConfig(opts);
|
|
1118
1144
|
const res = await fetch(`${config.apiUrl}/api/projects/${projectId}/volumes`, {
|
|
@@ -1122,15 +1148,36 @@ var Volume = class _Volume {
|
|
|
1122
1148
|
});
|
|
1123
1149
|
if (!res.ok) await handleApiError(res);
|
|
1124
1150
|
const vol = await res.json();
|
|
1125
|
-
return new _Volume({ volumeId: vol.id, ...opts });
|
|
1151
|
+
return new _Volume({ volumeId: vol.id, name: vol.name, ...opts });
|
|
1126
1152
|
}
|
|
1127
|
-
|
|
1153
|
+
/**
|
|
1154
|
+
* Return the project's volume with this name, creating it first if it does not
|
|
1155
|
+
* exist yet. This is the reason a volume's name is its key: an agent that wants
|
|
1156
|
+
* "the scratch disk for this task" no longer has to store an id between runs.
|
|
1157
|
+
*
|
|
1158
|
+
* An existing volume is returned as-is — `sizeGb` applies only to a fresh create
|
|
1159
|
+
* and never resizes one that is already there.
|
|
1160
|
+
*/
|
|
1161
|
+
static async getOrCreate(projectId, name, opts) {
|
|
1128
1162
|
const config = new ConnectionConfig(opts);
|
|
1129
|
-
const res = await fetch(`${config.apiUrl}/api/projects/${projectId}/volumes
|
|
1163
|
+
const res = await fetch(`${config.apiUrl}/api/projects/${projectId}/volumes`, {
|
|
1164
|
+
method: "POST",
|
|
1165
|
+
headers: config.headers,
|
|
1166
|
+
body: JSON.stringify({ name, sizeGb: opts?.sizeGb ?? 5, getOrCreate: true })
|
|
1167
|
+
});
|
|
1168
|
+
if (!res.ok) await handleApiError(res);
|
|
1169
|
+
const vol = await res.json();
|
|
1170
|
+
return new _Volume({ volumeId: vol.id, name: vol.name, ...opts });
|
|
1171
|
+
}
|
|
1172
|
+
/** Look a volume up by name or by id. */
|
|
1173
|
+
static async get(projectId, nameOrId, opts) {
|
|
1174
|
+
const config = new ConnectionConfig(opts);
|
|
1175
|
+
const res = await fetch(`${config.apiUrl}/api/projects/${projectId}/volumes/${encodeURIComponent(nameOrId)}`, {
|
|
1130
1176
|
headers: config.headers
|
|
1131
1177
|
});
|
|
1132
1178
|
if (!res.ok) await handleApiError(res);
|
|
1133
|
-
|
|
1179
|
+
const vol = await res.json();
|
|
1180
|
+
return new _Volume({ volumeId: vol.id, name: vol.name, ...opts });
|
|
1134
1181
|
}
|
|
1135
1182
|
static async list(projectId, opts) {
|
|
1136
1183
|
const config = new ConnectionConfig(opts);
|
|
@@ -1140,6 +1187,15 @@ var Volume = class _Volume {
|
|
|
1140
1187
|
if (!res.ok) await handleApiError(res);
|
|
1141
1188
|
return res.json();
|
|
1142
1189
|
}
|
|
1190
|
+
/** Delete a volume by name or by id, without constructing one first. */
|
|
1191
|
+
static async delete(projectId, nameOrId, opts) {
|
|
1192
|
+
const config = new ConnectionConfig(opts);
|
|
1193
|
+
const res = await fetch(`${config.apiUrl}/api/projects/${projectId}/volumes/${encodeURIComponent(nameOrId)}`, {
|
|
1194
|
+
method: "DELETE",
|
|
1195
|
+
headers: config.headers
|
|
1196
|
+
});
|
|
1197
|
+
if (!res.ok) await handleApiError(res);
|
|
1198
|
+
}
|
|
1143
1199
|
async getInfo(projectId) {
|
|
1144
1200
|
const res = await fetch(`${this.config.apiUrl}/api/projects/${projectId}/volumes/${this.volumeId}`, {
|
|
1145
1201
|
headers: this.config.headers
|
|
@@ -1159,6 +1215,7 @@ var Volume = class _Volume {
|
|
|
1159
1215
|
0 && (module.exports = {
|
|
1160
1216
|
AuthenticationError,
|
|
1161
1217
|
CodeSandbox,
|
|
1218
|
+
ConflictError,
|
|
1162
1219
|
DeployHandle,
|
|
1163
1220
|
Lizard,
|
|
1164
1221
|
LizardError,
|
package/dist/index.mjs
CHANGED
|
@@ -39,6 +39,12 @@ var NotFoundError = class extends LizardError {
|
|
|
39
39
|
this.name = "NotFoundError";
|
|
40
40
|
}
|
|
41
41
|
};
|
|
42
|
+
var ConflictError = class extends LizardError {
|
|
43
|
+
constructor(message = "Resource already exists") {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = "ConflictError";
|
|
46
|
+
}
|
|
47
|
+
};
|
|
42
48
|
var TimeoutError = class extends LizardError {
|
|
43
49
|
constructor(message = "Sandbox operation timed out") {
|
|
44
50
|
super(message);
|
|
@@ -55,6 +61,7 @@ async function handleApiError(res) {
|
|
|
55
61
|
}
|
|
56
62
|
if (res.status === 401 || res.status === 403) throw new AuthenticationError(message);
|
|
57
63
|
if (res.status === 404) throw new NotFoundError(message);
|
|
64
|
+
if (res.status === 409) throw new ConflictError(message);
|
|
58
65
|
if (res.status === 408 || res.status === 504) throw new TimeoutError(message);
|
|
59
66
|
throw new LizardError(`API error ${res.status}: ${message}`);
|
|
60
67
|
}
|
|
@@ -243,7 +250,8 @@ var SandboxClient = class {
|
|
|
243
250
|
projectId,
|
|
244
251
|
metadata: opts?.metadata,
|
|
245
252
|
envs: opts?.envs,
|
|
246
|
-
volumeId: opts?.volumeId
|
|
253
|
+
volumeId: opts?.volumeId,
|
|
254
|
+
volumeName: opts?.volumeName
|
|
247
255
|
})
|
|
248
256
|
});
|
|
249
257
|
if (!res.ok) await handleApiError(res);
|
|
@@ -372,9 +380,16 @@ var Sandbox = class extends SandboxClient {
|
|
|
372
380
|
* nothing is written to disk, so a paused sandbox does not survive a host
|
|
373
381
|
* failure. Resume with `sandbox.resume()` or `Sandbox.connect(id)`.
|
|
374
382
|
*
|
|
375
|
-
* Pausing does not
|
|
376
|
-
*
|
|
377
|
-
*
|
|
383
|
+
* Pausing does not buy you time past the original deadline. Resuming pushes the
|
|
384
|
+
* expiry forward by however long the sandbox was paused — so a pause/resume cycle
|
|
385
|
+
* costs no runtime — but that adjustment only happens on `resume()`. A sandbox left
|
|
386
|
+
* paused past its original `expiresAt` is deleted there, within a minute, and
|
|
387
|
+
* `resume()` then returns `false`.
|
|
388
|
+
*
|
|
389
|
+
* In other words: pause and come back *before* the original deadline and you lose
|
|
390
|
+
* nothing; leave it paused across the deadline and it is gone. Pass `timeoutMs: 0`
|
|
391
|
+
* at create time to opt out of expiry entirely, which is the only way to park a
|
|
392
|
+
* sandbox indefinitely.
|
|
378
393
|
*
|
|
379
394
|
* @returns `true` if paused successfully.
|
|
380
395
|
*/
|
|
@@ -384,7 +399,12 @@ var Sandbox = class extends SandboxClient {
|
|
|
384
399
|
/**
|
|
385
400
|
* Resume a paused sandbox by unfreezing its vCPUs.
|
|
386
401
|
*
|
|
387
|
-
*
|
|
402
|
+
* The expiry is pushed forward by the time spent paused, so the timeout measures
|
|
403
|
+
* running time rather than wall-clock — provided the original deadline had not
|
|
404
|
+
* already passed while paused. See {@link pause}.
|
|
405
|
+
*
|
|
406
|
+
* @returns `true` if resumed successfully, `false` if the sandbox no longer exists
|
|
407
|
+
* (including because it expired while paused).
|
|
388
408
|
*/
|
|
389
409
|
async resume(opts) {
|
|
390
410
|
return SandboxClient.resumeSandbox(this.sandboxId, this.resolveOpts(opts));
|
|
@@ -1076,8 +1096,13 @@ async function* readLines(body) {
|
|
|
1076
1096
|
var Volume = class _Volume {
|
|
1077
1097
|
constructor(opts) {
|
|
1078
1098
|
this.volumeId = opts.volumeId;
|
|
1099
|
+
this.name = opts.name;
|
|
1079
1100
|
this.config = new ConnectionConfig(opts);
|
|
1080
1101
|
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Create a volume. Throws `ConflictError` (409) if the project already has one with
|
|
1104
|
+
* this name — use {@link getOrCreate} when you want "make sure this exists" instead.
|
|
1105
|
+
*/
|
|
1081
1106
|
static async create(projectId, name, opts) {
|
|
1082
1107
|
const config = new ConnectionConfig(opts);
|
|
1083
1108
|
const res = await fetch(`${config.apiUrl}/api/projects/${projectId}/volumes`, {
|
|
@@ -1087,15 +1112,36 @@ var Volume = class _Volume {
|
|
|
1087
1112
|
});
|
|
1088
1113
|
if (!res.ok) await handleApiError(res);
|
|
1089
1114
|
const vol = await res.json();
|
|
1090
|
-
return new _Volume({ volumeId: vol.id, ...opts });
|
|
1115
|
+
return new _Volume({ volumeId: vol.id, name: vol.name, ...opts });
|
|
1091
1116
|
}
|
|
1092
|
-
|
|
1117
|
+
/**
|
|
1118
|
+
* Return the project's volume with this name, creating it first if it does not
|
|
1119
|
+
* exist yet. This is the reason a volume's name is its key: an agent that wants
|
|
1120
|
+
* "the scratch disk for this task" no longer has to store an id between runs.
|
|
1121
|
+
*
|
|
1122
|
+
* An existing volume is returned as-is — `sizeGb` applies only to a fresh create
|
|
1123
|
+
* and never resizes one that is already there.
|
|
1124
|
+
*/
|
|
1125
|
+
static async getOrCreate(projectId, name, opts) {
|
|
1093
1126
|
const config = new ConnectionConfig(opts);
|
|
1094
|
-
const res = await fetch(`${config.apiUrl}/api/projects/${projectId}/volumes
|
|
1127
|
+
const res = await fetch(`${config.apiUrl}/api/projects/${projectId}/volumes`, {
|
|
1128
|
+
method: "POST",
|
|
1129
|
+
headers: config.headers,
|
|
1130
|
+
body: JSON.stringify({ name, sizeGb: opts?.sizeGb ?? 5, getOrCreate: true })
|
|
1131
|
+
});
|
|
1132
|
+
if (!res.ok) await handleApiError(res);
|
|
1133
|
+
const vol = await res.json();
|
|
1134
|
+
return new _Volume({ volumeId: vol.id, name: vol.name, ...opts });
|
|
1135
|
+
}
|
|
1136
|
+
/** Look a volume up by name or by id. */
|
|
1137
|
+
static async get(projectId, nameOrId, opts) {
|
|
1138
|
+
const config = new ConnectionConfig(opts);
|
|
1139
|
+
const res = await fetch(`${config.apiUrl}/api/projects/${projectId}/volumes/${encodeURIComponent(nameOrId)}`, {
|
|
1095
1140
|
headers: config.headers
|
|
1096
1141
|
});
|
|
1097
1142
|
if (!res.ok) await handleApiError(res);
|
|
1098
|
-
|
|
1143
|
+
const vol = await res.json();
|
|
1144
|
+
return new _Volume({ volumeId: vol.id, name: vol.name, ...opts });
|
|
1099
1145
|
}
|
|
1100
1146
|
static async list(projectId, opts) {
|
|
1101
1147
|
const config = new ConnectionConfig(opts);
|
|
@@ -1105,6 +1151,15 @@ var Volume = class _Volume {
|
|
|
1105
1151
|
if (!res.ok) await handleApiError(res);
|
|
1106
1152
|
return res.json();
|
|
1107
1153
|
}
|
|
1154
|
+
/** Delete a volume by name or by id, without constructing one first. */
|
|
1155
|
+
static async delete(projectId, nameOrId, opts) {
|
|
1156
|
+
const config = new ConnectionConfig(opts);
|
|
1157
|
+
const res = await fetch(`${config.apiUrl}/api/projects/${projectId}/volumes/${encodeURIComponent(nameOrId)}`, {
|
|
1158
|
+
method: "DELETE",
|
|
1159
|
+
headers: config.headers
|
|
1160
|
+
});
|
|
1161
|
+
if (!res.ok) await handleApiError(res);
|
|
1162
|
+
}
|
|
1108
1163
|
async getInfo(projectId) {
|
|
1109
1164
|
const res = await fetch(`${this.config.apiUrl}/api/projects/${projectId}/volumes/${this.volumeId}`, {
|
|
1110
1165
|
headers: this.config.headers
|
|
@@ -1123,6 +1178,7 @@ var Volume = class _Volume {
|
|
|
1123
1178
|
export {
|
|
1124
1179
|
AuthenticationError,
|
|
1125
1180
|
CodeSandbox,
|
|
1181
|
+
ConflictError,
|
|
1126
1182
|
DeployHandle,
|
|
1127
1183
|
Lizard,
|
|
1128
1184
|
LizardError,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lizard-build/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"description": "Firecracker microVM sandboxes for AI agents — boot Linux in milliseconds, run code, write files, expose ports over HTTPS, then pause a session with memory intact and resume it later. TypeScript SDK for Lizard Sandboxes (lizard.build).",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|