freestyle-sandboxes 0.1.2 → 0.1.3
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/index.cjs +117 -13
- package/index.d.cts +29 -6
- package/index.d.mts +29 -6
- package/index.mjs +117 -13
- package/package.json +1 -1
package/index.cjs
CHANGED
|
@@ -3853,6 +3853,14 @@ class FileSystem {
|
|
|
3853
3853
|
this.vmId = vmId;
|
|
3854
3854
|
this.client = client;
|
|
3855
3855
|
}
|
|
3856
|
+
vm = null;
|
|
3857
|
+
/**
|
|
3858
|
+
* @internal
|
|
3859
|
+
* Set the parent VM instance for exec operations
|
|
3860
|
+
*/
|
|
3861
|
+
_setVm(vm) {
|
|
3862
|
+
this.vm = vm;
|
|
3863
|
+
}
|
|
3856
3864
|
/**
|
|
3857
3865
|
* Read a file from the VM as a buffer.
|
|
3858
3866
|
* @param filepath The path to the file in the VM
|
|
@@ -3910,21 +3918,47 @@ class FileSystem {
|
|
|
3910
3918
|
* @returns Array of file/directory names
|
|
3911
3919
|
*/
|
|
3912
3920
|
async readDir(path) {
|
|
3913
|
-
|
|
3921
|
+
const response = await this.client.get("/v1/vms/{vm_id}/files/{filepath}", {
|
|
3922
|
+
params: { vm_id: this.vmId, filepath: path }
|
|
3923
|
+
});
|
|
3924
|
+
if ("files" in response && response.files) {
|
|
3925
|
+
return response.files;
|
|
3926
|
+
}
|
|
3927
|
+
throw new Error("Path is not a directory");
|
|
3914
3928
|
}
|
|
3915
3929
|
/**
|
|
3916
3930
|
* Create a directory in the VM.
|
|
3917
3931
|
* @param path The path to create
|
|
3932
|
+
* @param recursive Create parent directories if needed (default: false)
|
|
3918
3933
|
*/
|
|
3919
|
-
async mkdir(path) {
|
|
3920
|
-
|
|
3934
|
+
async mkdir(path, recursive = false) {
|
|
3935
|
+
if (!this.vm) {
|
|
3936
|
+
throw new Error("FileSystem not properly initialized");
|
|
3937
|
+
}
|
|
3938
|
+
const flag = recursive ? "-p" : "";
|
|
3939
|
+
const result = await this.vm.exec(`mkdir ${flag} "${path}"`);
|
|
3940
|
+
if (result.statusCode !== 0) {
|
|
3941
|
+
throw new Error(
|
|
3942
|
+
`Failed to create directory: ${result.stderr || "Unknown error"}`
|
|
3943
|
+
);
|
|
3944
|
+
}
|
|
3921
3945
|
}
|
|
3922
3946
|
/**
|
|
3923
3947
|
* Remove a file or directory from the VM.
|
|
3924
3948
|
* @param path The path to remove
|
|
3949
|
+
* @param recursive Remove directories and their contents recursively (default: false)
|
|
3925
3950
|
*/
|
|
3926
|
-
async remove(path) {
|
|
3927
|
-
|
|
3951
|
+
async remove(path, recursive = false) {
|
|
3952
|
+
if (!this.vm) {
|
|
3953
|
+
throw new Error("FileSystem not properly initialized");
|
|
3954
|
+
}
|
|
3955
|
+
const flag = recursive ? "-rf" : "-f";
|
|
3956
|
+
const result = await this.vm.exec(`rm ${flag} "${path}"`);
|
|
3957
|
+
if (result.statusCode !== 0) {
|
|
3958
|
+
throw new Error(
|
|
3959
|
+
`Failed to remove path: ${result.stderr || "Unknown error"}`
|
|
3960
|
+
);
|
|
3961
|
+
}
|
|
3928
3962
|
}
|
|
3929
3963
|
/**
|
|
3930
3964
|
* Check if a file or directory exists in the VM.
|
|
@@ -3932,7 +3966,11 @@ class FileSystem {
|
|
|
3932
3966
|
* @returns True if exists, false otherwise
|
|
3933
3967
|
*/
|
|
3934
3968
|
async exists(path) {
|
|
3935
|
-
|
|
3969
|
+
if (!this.vm) {
|
|
3970
|
+
throw new Error("FileSystem not properly initialized");
|
|
3971
|
+
}
|
|
3972
|
+
const result = await this.vm.exec(`test -e "${path}"`);
|
|
3973
|
+
return result.statusCode === 0;
|
|
3936
3974
|
}
|
|
3937
3975
|
/**
|
|
3938
3976
|
* Get file/directory stats.
|
|
@@ -3940,7 +3978,37 @@ class FileSystem {
|
|
|
3940
3978
|
* @returns File/directory stats
|
|
3941
3979
|
*/
|
|
3942
3980
|
async stat(path) {
|
|
3943
|
-
|
|
3981
|
+
if (!this.vm) {
|
|
3982
|
+
throw new Error("FileSystem not properly initialized");
|
|
3983
|
+
}
|
|
3984
|
+
const result = await this.vm.exec(
|
|
3985
|
+
`stat -c '%s|%F|%a|%U|%G|%y' "${path}" 2>/dev/null || stat -f '%z|%HT|%p|%Su|%Sg|%Sm' "${path}"`
|
|
3986
|
+
);
|
|
3987
|
+
if (result.statusCode !== 0 || !result.stdout) {
|
|
3988
|
+
throw new Error(
|
|
3989
|
+
`Failed to stat path: ${result.stderr || "Unknown error"}`
|
|
3990
|
+
);
|
|
3991
|
+
}
|
|
3992
|
+
const parts = result.stdout.trim().split("|");
|
|
3993
|
+
if (parts.length < 6) {
|
|
3994
|
+
throw new Error(`Invalid stat output: ${result.stdout}`);
|
|
3995
|
+
}
|
|
3996
|
+
const size = parseInt(parts[0] || "0");
|
|
3997
|
+
const fileType = parts[1] || "";
|
|
3998
|
+
const permissions = parts[2] || "";
|
|
3999
|
+
const owner = parts[3] || "";
|
|
4000
|
+
const group = parts[4] || "";
|
|
4001
|
+
const modified = parts[5] || "";
|
|
4002
|
+
return {
|
|
4003
|
+
size,
|
|
4004
|
+
isFile: fileType.includes("regular file") || fileType.includes("Regular File"),
|
|
4005
|
+
isDirectory: fileType.includes("directory") || fileType.includes("Directory"),
|
|
4006
|
+
isSymlink: fileType.includes("symbolic link") || fileType.includes("Symbolic Link"),
|
|
4007
|
+
permissions,
|
|
4008
|
+
owner,
|
|
4009
|
+
group,
|
|
4010
|
+
modified
|
|
4011
|
+
};
|
|
3944
4012
|
}
|
|
3945
4013
|
/**
|
|
3946
4014
|
* Watch for file changes in the VM.
|
|
@@ -4125,8 +4193,8 @@ function composeCreateVmOptions(arr) {
|
|
|
4125
4193
|
if (!result.template) {
|
|
4126
4194
|
result.template = options.template;
|
|
4127
4195
|
} else if (options.template !== null) {
|
|
4128
|
-
const baseTemplate = result.template instanceof VmTemplate ? result.template["
|
|
4129
|
-
const newTemplate = options.template instanceof VmTemplate ? options.template["
|
|
4196
|
+
const baseTemplate = result.template instanceof VmTemplate ? result.template["raw"] : result.template;
|
|
4197
|
+
const newTemplate = options.template instanceof VmTemplate ? options.template["raw"] : options.template;
|
|
4130
4198
|
result.template = {
|
|
4131
4199
|
// Simple overrides - start with base, override with new
|
|
4132
4200
|
snapshotId: newTemplate.snapshotId !== void 0 ? newTemplate.snapshotId : baseTemplate.snapshotId,
|
|
@@ -4294,6 +4362,7 @@ class Vm {
|
|
|
4294
4362
|
this.apiClient = apiClient;
|
|
4295
4363
|
this.vmId = vmId;
|
|
4296
4364
|
this.fs = new FileSystem(vmId, apiClient);
|
|
4365
|
+
this.fs._setVm(this);
|
|
4297
4366
|
this.terminals = new VmTerminals(vmId, apiClient);
|
|
4298
4367
|
}
|
|
4299
4368
|
vmId;
|
|
@@ -4424,15 +4493,44 @@ class VmSnapshotsNamespace {
|
|
|
4424
4493
|
this.apiClient = apiClient;
|
|
4425
4494
|
}
|
|
4426
4495
|
async ensure(options) {
|
|
4496
|
+
if (options.template.raw.template instanceof VmTemplate) {
|
|
4497
|
+
options.template = await ensureNestedTemplates(options.template, this);
|
|
4498
|
+
}
|
|
4427
4499
|
return this.apiClient.post("/v1/vms/snapshots", {
|
|
4428
4500
|
body: {
|
|
4429
4501
|
...options,
|
|
4430
4502
|
// @ts-ignore
|
|
4431
|
-
template: options.template?.
|
|
4503
|
+
template: options.template?.raw
|
|
4432
4504
|
}
|
|
4433
4505
|
});
|
|
4434
4506
|
}
|
|
4435
4507
|
}
|
|
4508
|
+
async function ensureNestedTemplates(template, snapshots) {
|
|
4509
|
+
let templates = [template];
|
|
4510
|
+
while (templates.at(-1)?.raw.template instanceof VmTemplate) {
|
|
4511
|
+
let innerTemplate = templates.at(-1).raw.template;
|
|
4512
|
+
templates.at(-1).raw.template = void 0;
|
|
4513
|
+
templates.push(innerTemplate);
|
|
4514
|
+
}
|
|
4515
|
+
return await layerTemplates(templates, snapshots);
|
|
4516
|
+
}
|
|
4517
|
+
async function layerTemplates(templates, snapshots) {
|
|
4518
|
+
let lastTemplate = templates.pop();
|
|
4519
|
+
const { snapshotId } = await snapshots.ensure({
|
|
4520
|
+
template: lastTemplate
|
|
4521
|
+
});
|
|
4522
|
+
let lastSnapshotId = snapshotId;
|
|
4523
|
+
while (templates.length > 0) {
|
|
4524
|
+
const template = templates.pop();
|
|
4525
|
+
template.raw.snapshotId = lastSnapshotId;
|
|
4526
|
+
const { snapshotId: snapshotId2 } = await snapshots.ensure({
|
|
4527
|
+
template
|
|
4528
|
+
});
|
|
4529
|
+
lastSnapshotId = snapshotId2;
|
|
4530
|
+
lastTemplate = template;
|
|
4531
|
+
}
|
|
4532
|
+
return lastTemplate;
|
|
4533
|
+
}
|
|
4436
4534
|
class VmsNamespace {
|
|
4437
4535
|
constructor(apiClient) {
|
|
4438
4536
|
this.apiClient = apiClient;
|
|
@@ -4448,6 +4546,12 @@ class VmsNamespace {
|
|
|
4448
4546
|
const builders = options.with || {};
|
|
4449
4547
|
const { with: _, ...baseConfig } = options;
|
|
4450
4548
|
let config = baseConfig;
|
|
4549
|
+
if (config.template instanceof VmTemplate) {
|
|
4550
|
+
config.template = await ensureNestedTemplates(
|
|
4551
|
+
config.template,
|
|
4552
|
+
this.snapshots
|
|
4553
|
+
);
|
|
4554
|
+
}
|
|
4451
4555
|
const keys = Object.keys(builders);
|
|
4452
4556
|
for (const key of keys) {
|
|
4453
4557
|
const builder = builders[key];
|
|
@@ -4458,7 +4562,7 @@ class VmsNamespace {
|
|
|
4458
4562
|
const response = await this.apiClient.post("/v1/vms", {
|
|
4459
4563
|
body: {
|
|
4460
4564
|
...config,
|
|
4461
|
-
template: config.template instanceof VmTemplate ? config.template["
|
|
4565
|
+
template: config.template instanceof VmTemplate ? config.template["raw"] : config.template
|
|
4462
4566
|
}
|
|
4463
4567
|
});
|
|
4464
4568
|
const vmId = response.id;
|
|
@@ -4504,9 +4608,9 @@ class VmsNamespace {
|
|
|
4504
4608
|
}
|
|
4505
4609
|
}
|
|
4506
4610
|
class VmTemplate {
|
|
4507
|
-
|
|
4611
|
+
raw;
|
|
4508
4612
|
constructor(template) {
|
|
4509
|
-
this.
|
|
4613
|
+
this.raw = template;
|
|
4510
4614
|
}
|
|
4511
4615
|
}
|
|
4512
4616
|
|
package/index.d.cts
CHANGED
|
@@ -9349,7 +9349,13 @@ interface CreateSnapshotRequest {
|
|
|
9349
9349
|
declare class FileSystem {
|
|
9350
9350
|
private vmId;
|
|
9351
9351
|
private client;
|
|
9352
|
+
private vm;
|
|
9352
9353
|
constructor(vmId: string, client: ApiClient);
|
|
9354
|
+
/**
|
|
9355
|
+
* @internal
|
|
9356
|
+
* Set the parent VM instance for exec operations
|
|
9357
|
+
*/
|
|
9358
|
+
_setVm(vm: Vm): void;
|
|
9353
9359
|
/**
|
|
9354
9360
|
* Read a file from the VM as a buffer.
|
|
9355
9361
|
* @param filepath The path to the file in the VM
|
|
@@ -9381,17 +9387,22 @@ declare class FileSystem {
|
|
|
9381
9387
|
* @param path The path to the directory in the VM
|
|
9382
9388
|
* @returns Array of file/directory names
|
|
9383
9389
|
*/
|
|
9384
|
-
readDir(path: string): Promise<
|
|
9390
|
+
readDir(path: string): Promise<{
|
|
9391
|
+
name: string;
|
|
9392
|
+
kind: string;
|
|
9393
|
+
}[]>;
|
|
9385
9394
|
/**
|
|
9386
9395
|
* Create a directory in the VM.
|
|
9387
9396
|
* @param path The path to create
|
|
9397
|
+
* @param recursive Create parent directories if needed (default: false)
|
|
9388
9398
|
*/
|
|
9389
|
-
mkdir(path: string): Promise<void>;
|
|
9399
|
+
mkdir(path: string, recursive?: boolean): Promise<void>;
|
|
9390
9400
|
/**
|
|
9391
9401
|
* Remove a file or directory from the VM.
|
|
9392
9402
|
* @param path The path to remove
|
|
9403
|
+
* @param recursive Remove directories and their contents recursively (default: false)
|
|
9393
9404
|
*/
|
|
9394
|
-
remove(path: string): Promise<void>;
|
|
9405
|
+
remove(path: string, recursive?: boolean): Promise<void>;
|
|
9395
9406
|
/**
|
|
9396
9407
|
* Check if a file or directory exists in the VM.
|
|
9397
9408
|
* @param path The path to check
|
|
@@ -9403,7 +9414,16 @@ declare class FileSystem {
|
|
|
9403
9414
|
* @param path The path to stat
|
|
9404
9415
|
* @returns File/directory stats
|
|
9405
9416
|
*/
|
|
9406
|
-
stat(path: string): Promise<
|
|
9417
|
+
stat(path: string): Promise<{
|
|
9418
|
+
size: number;
|
|
9419
|
+
isFile: boolean;
|
|
9420
|
+
isDirectory: boolean;
|
|
9421
|
+
isSymlink: boolean;
|
|
9422
|
+
permissions: string;
|
|
9423
|
+
owner: string;
|
|
9424
|
+
group: string;
|
|
9425
|
+
modified: string;
|
|
9426
|
+
}>;
|
|
9407
9427
|
/**
|
|
9408
9428
|
* Watch for file changes in the VM.
|
|
9409
9429
|
* @returns An async generator that yields file change events
|
|
@@ -9606,9 +9626,12 @@ declare class VmsNamespace {
|
|
|
9606
9626
|
vmId: string;
|
|
9607
9627
|
}): Promise<ResponseDeleteV1VmsVmId200>;
|
|
9608
9628
|
}
|
|
9629
|
+
type TemplateOptions = CreateSnapshotRequest["template"] & {
|
|
9630
|
+
template?: VmTemplate;
|
|
9631
|
+
};
|
|
9609
9632
|
declare class VmTemplate {
|
|
9610
|
-
|
|
9611
|
-
constructor(template:
|
|
9633
|
+
raw: TemplateOptions;
|
|
9634
|
+
constructor(template: TemplateOptions);
|
|
9612
9635
|
}
|
|
9613
9636
|
|
|
9614
9637
|
type FreestyleOptions = ApiClientConfig;
|
package/index.d.mts
CHANGED
|
@@ -9349,7 +9349,13 @@ interface CreateSnapshotRequest {
|
|
|
9349
9349
|
declare class FileSystem {
|
|
9350
9350
|
private vmId;
|
|
9351
9351
|
private client;
|
|
9352
|
+
private vm;
|
|
9352
9353
|
constructor(vmId: string, client: ApiClient);
|
|
9354
|
+
/**
|
|
9355
|
+
* @internal
|
|
9356
|
+
* Set the parent VM instance for exec operations
|
|
9357
|
+
*/
|
|
9358
|
+
_setVm(vm: Vm): void;
|
|
9353
9359
|
/**
|
|
9354
9360
|
* Read a file from the VM as a buffer.
|
|
9355
9361
|
* @param filepath The path to the file in the VM
|
|
@@ -9381,17 +9387,22 @@ declare class FileSystem {
|
|
|
9381
9387
|
* @param path The path to the directory in the VM
|
|
9382
9388
|
* @returns Array of file/directory names
|
|
9383
9389
|
*/
|
|
9384
|
-
readDir(path: string): Promise<
|
|
9390
|
+
readDir(path: string): Promise<{
|
|
9391
|
+
name: string;
|
|
9392
|
+
kind: string;
|
|
9393
|
+
}[]>;
|
|
9385
9394
|
/**
|
|
9386
9395
|
* Create a directory in the VM.
|
|
9387
9396
|
* @param path The path to create
|
|
9397
|
+
* @param recursive Create parent directories if needed (default: false)
|
|
9388
9398
|
*/
|
|
9389
|
-
mkdir(path: string): Promise<void>;
|
|
9399
|
+
mkdir(path: string, recursive?: boolean): Promise<void>;
|
|
9390
9400
|
/**
|
|
9391
9401
|
* Remove a file or directory from the VM.
|
|
9392
9402
|
* @param path The path to remove
|
|
9403
|
+
* @param recursive Remove directories and their contents recursively (default: false)
|
|
9393
9404
|
*/
|
|
9394
|
-
remove(path: string): Promise<void>;
|
|
9405
|
+
remove(path: string, recursive?: boolean): Promise<void>;
|
|
9395
9406
|
/**
|
|
9396
9407
|
* Check if a file or directory exists in the VM.
|
|
9397
9408
|
* @param path The path to check
|
|
@@ -9403,7 +9414,16 @@ declare class FileSystem {
|
|
|
9403
9414
|
* @param path The path to stat
|
|
9404
9415
|
* @returns File/directory stats
|
|
9405
9416
|
*/
|
|
9406
|
-
stat(path: string): Promise<
|
|
9417
|
+
stat(path: string): Promise<{
|
|
9418
|
+
size: number;
|
|
9419
|
+
isFile: boolean;
|
|
9420
|
+
isDirectory: boolean;
|
|
9421
|
+
isSymlink: boolean;
|
|
9422
|
+
permissions: string;
|
|
9423
|
+
owner: string;
|
|
9424
|
+
group: string;
|
|
9425
|
+
modified: string;
|
|
9426
|
+
}>;
|
|
9407
9427
|
/**
|
|
9408
9428
|
* Watch for file changes in the VM.
|
|
9409
9429
|
* @returns An async generator that yields file change events
|
|
@@ -9606,9 +9626,12 @@ declare class VmsNamespace {
|
|
|
9606
9626
|
vmId: string;
|
|
9607
9627
|
}): Promise<ResponseDeleteV1VmsVmId200>;
|
|
9608
9628
|
}
|
|
9629
|
+
type TemplateOptions = CreateSnapshotRequest["template"] & {
|
|
9630
|
+
template?: VmTemplate;
|
|
9631
|
+
};
|
|
9609
9632
|
declare class VmTemplate {
|
|
9610
|
-
|
|
9611
|
-
constructor(template:
|
|
9633
|
+
raw: TemplateOptions;
|
|
9634
|
+
constructor(template: TemplateOptions);
|
|
9612
9635
|
}
|
|
9613
9636
|
|
|
9614
9637
|
type FreestyleOptions = ApiClientConfig;
|
package/index.mjs
CHANGED
|
@@ -3851,6 +3851,14 @@ class FileSystem {
|
|
|
3851
3851
|
this.vmId = vmId;
|
|
3852
3852
|
this.client = client;
|
|
3853
3853
|
}
|
|
3854
|
+
vm = null;
|
|
3855
|
+
/**
|
|
3856
|
+
* @internal
|
|
3857
|
+
* Set the parent VM instance for exec operations
|
|
3858
|
+
*/
|
|
3859
|
+
_setVm(vm) {
|
|
3860
|
+
this.vm = vm;
|
|
3861
|
+
}
|
|
3854
3862
|
/**
|
|
3855
3863
|
* Read a file from the VM as a buffer.
|
|
3856
3864
|
* @param filepath The path to the file in the VM
|
|
@@ -3908,21 +3916,47 @@ class FileSystem {
|
|
|
3908
3916
|
* @returns Array of file/directory names
|
|
3909
3917
|
*/
|
|
3910
3918
|
async readDir(path) {
|
|
3911
|
-
|
|
3919
|
+
const response = await this.client.get("/v1/vms/{vm_id}/files/{filepath}", {
|
|
3920
|
+
params: { vm_id: this.vmId, filepath: path }
|
|
3921
|
+
});
|
|
3922
|
+
if ("files" in response && response.files) {
|
|
3923
|
+
return response.files;
|
|
3924
|
+
}
|
|
3925
|
+
throw new Error("Path is not a directory");
|
|
3912
3926
|
}
|
|
3913
3927
|
/**
|
|
3914
3928
|
* Create a directory in the VM.
|
|
3915
3929
|
* @param path The path to create
|
|
3930
|
+
* @param recursive Create parent directories if needed (default: false)
|
|
3916
3931
|
*/
|
|
3917
|
-
async mkdir(path) {
|
|
3918
|
-
|
|
3932
|
+
async mkdir(path, recursive = false) {
|
|
3933
|
+
if (!this.vm) {
|
|
3934
|
+
throw new Error("FileSystem not properly initialized");
|
|
3935
|
+
}
|
|
3936
|
+
const flag = recursive ? "-p" : "";
|
|
3937
|
+
const result = await this.vm.exec(`mkdir ${flag} "${path}"`);
|
|
3938
|
+
if (result.statusCode !== 0) {
|
|
3939
|
+
throw new Error(
|
|
3940
|
+
`Failed to create directory: ${result.stderr || "Unknown error"}`
|
|
3941
|
+
);
|
|
3942
|
+
}
|
|
3919
3943
|
}
|
|
3920
3944
|
/**
|
|
3921
3945
|
* Remove a file or directory from the VM.
|
|
3922
3946
|
* @param path The path to remove
|
|
3947
|
+
* @param recursive Remove directories and their contents recursively (default: false)
|
|
3923
3948
|
*/
|
|
3924
|
-
async remove(path) {
|
|
3925
|
-
|
|
3949
|
+
async remove(path, recursive = false) {
|
|
3950
|
+
if (!this.vm) {
|
|
3951
|
+
throw new Error("FileSystem not properly initialized");
|
|
3952
|
+
}
|
|
3953
|
+
const flag = recursive ? "-rf" : "-f";
|
|
3954
|
+
const result = await this.vm.exec(`rm ${flag} "${path}"`);
|
|
3955
|
+
if (result.statusCode !== 0) {
|
|
3956
|
+
throw new Error(
|
|
3957
|
+
`Failed to remove path: ${result.stderr || "Unknown error"}`
|
|
3958
|
+
);
|
|
3959
|
+
}
|
|
3926
3960
|
}
|
|
3927
3961
|
/**
|
|
3928
3962
|
* Check if a file or directory exists in the VM.
|
|
@@ -3930,7 +3964,11 @@ class FileSystem {
|
|
|
3930
3964
|
* @returns True if exists, false otherwise
|
|
3931
3965
|
*/
|
|
3932
3966
|
async exists(path) {
|
|
3933
|
-
|
|
3967
|
+
if (!this.vm) {
|
|
3968
|
+
throw new Error("FileSystem not properly initialized");
|
|
3969
|
+
}
|
|
3970
|
+
const result = await this.vm.exec(`test -e "${path}"`);
|
|
3971
|
+
return result.statusCode === 0;
|
|
3934
3972
|
}
|
|
3935
3973
|
/**
|
|
3936
3974
|
* Get file/directory stats.
|
|
@@ -3938,7 +3976,37 @@ class FileSystem {
|
|
|
3938
3976
|
* @returns File/directory stats
|
|
3939
3977
|
*/
|
|
3940
3978
|
async stat(path) {
|
|
3941
|
-
|
|
3979
|
+
if (!this.vm) {
|
|
3980
|
+
throw new Error("FileSystem not properly initialized");
|
|
3981
|
+
}
|
|
3982
|
+
const result = await this.vm.exec(
|
|
3983
|
+
`stat -c '%s|%F|%a|%U|%G|%y' "${path}" 2>/dev/null || stat -f '%z|%HT|%p|%Su|%Sg|%Sm' "${path}"`
|
|
3984
|
+
);
|
|
3985
|
+
if (result.statusCode !== 0 || !result.stdout) {
|
|
3986
|
+
throw new Error(
|
|
3987
|
+
`Failed to stat path: ${result.stderr || "Unknown error"}`
|
|
3988
|
+
);
|
|
3989
|
+
}
|
|
3990
|
+
const parts = result.stdout.trim().split("|");
|
|
3991
|
+
if (parts.length < 6) {
|
|
3992
|
+
throw new Error(`Invalid stat output: ${result.stdout}`);
|
|
3993
|
+
}
|
|
3994
|
+
const size = parseInt(parts[0] || "0");
|
|
3995
|
+
const fileType = parts[1] || "";
|
|
3996
|
+
const permissions = parts[2] || "";
|
|
3997
|
+
const owner = parts[3] || "";
|
|
3998
|
+
const group = parts[4] || "";
|
|
3999
|
+
const modified = parts[5] || "";
|
|
4000
|
+
return {
|
|
4001
|
+
size,
|
|
4002
|
+
isFile: fileType.includes("regular file") || fileType.includes("Regular File"),
|
|
4003
|
+
isDirectory: fileType.includes("directory") || fileType.includes("Directory"),
|
|
4004
|
+
isSymlink: fileType.includes("symbolic link") || fileType.includes("Symbolic Link"),
|
|
4005
|
+
permissions,
|
|
4006
|
+
owner,
|
|
4007
|
+
group,
|
|
4008
|
+
modified
|
|
4009
|
+
};
|
|
3942
4010
|
}
|
|
3943
4011
|
/**
|
|
3944
4012
|
* Watch for file changes in the VM.
|
|
@@ -4123,8 +4191,8 @@ function composeCreateVmOptions(arr) {
|
|
|
4123
4191
|
if (!result.template) {
|
|
4124
4192
|
result.template = options.template;
|
|
4125
4193
|
} else if (options.template !== null) {
|
|
4126
|
-
const baseTemplate = result.template instanceof VmTemplate ? result.template["
|
|
4127
|
-
const newTemplate = options.template instanceof VmTemplate ? options.template["
|
|
4194
|
+
const baseTemplate = result.template instanceof VmTemplate ? result.template["raw"] : result.template;
|
|
4195
|
+
const newTemplate = options.template instanceof VmTemplate ? options.template["raw"] : options.template;
|
|
4128
4196
|
result.template = {
|
|
4129
4197
|
// Simple overrides - start with base, override with new
|
|
4130
4198
|
snapshotId: newTemplate.snapshotId !== void 0 ? newTemplate.snapshotId : baseTemplate.snapshotId,
|
|
@@ -4292,6 +4360,7 @@ class Vm {
|
|
|
4292
4360
|
this.apiClient = apiClient;
|
|
4293
4361
|
this.vmId = vmId;
|
|
4294
4362
|
this.fs = new FileSystem(vmId, apiClient);
|
|
4363
|
+
this.fs._setVm(this);
|
|
4295
4364
|
this.terminals = new VmTerminals(vmId, apiClient);
|
|
4296
4365
|
}
|
|
4297
4366
|
vmId;
|
|
@@ -4422,15 +4491,44 @@ class VmSnapshotsNamespace {
|
|
|
4422
4491
|
this.apiClient = apiClient;
|
|
4423
4492
|
}
|
|
4424
4493
|
async ensure(options) {
|
|
4494
|
+
if (options.template.raw.template instanceof VmTemplate) {
|
|
4495
|
+
options.template = await ensureNestedTemplates(options.template, this);
|
|
4496
|
+
}
|
|
4425
4497
|
return this.apiClient.post("/v1/vms/snapshots", {
|
|
4426
4498
|
body: {
|
|
4427
4499
|
...options,
|
|
4428
4500
|
// @ts-ignore
|
|
4429
|
-
template: options.template?.
|
|
4501
|
+
template: options.template?.raw
|
|
4430
4502
|
}
|
|
4431
4503
|
});
|
|
4432
4504
|
}
|
|
4433
4505
|
}
|
|
4506
|
+
async function ensureNestedTemplates(template, snapshots) {
|
|
4507
|
+
let templates = [template];
|
|
4508
|
+
while (templates.at(-1)?.raw.template instanceof VmTemplate) {
|
|
4509
|
+
let innerTemplate = templates.at(-1).raw.template;
|
|
4510
|
+
templates.at(-1).raw.template = void 0;
|
|
4511
|
+
templates.push(innerTemplate);
|
|
4512
|
+
}
|
|
4513
|
+
return await layerTemplates(templates, snapshots);
|
|
4514
|
+
}
|
|
4515
|
+
async function layerTemplates(templates, snapshots) {
|
|
4516
|
+
let lastTemplate = templates.pop();
|
|
4517
|
+
const { snapshotId } = await snapshots.ensure({
|
|
4518
|
+
template: lastTemplate
|
|
4519
|
+
});
|
|
4520
|
+
let lastSnapshotId = snapshotId;
|
|
4521
|
+
while (templates.length > 0) {
|
|
4522
|
+
const template = templates.pop();
|
|
4523
|
+
template.raw.snapshotId = lastSnapshotId;
|
|
4524
|
+
const { snapshotId: snapshotId2 } = await snapshots.ensure({
|
|
4525
|
+
template
|
|
4526
|
+
});
|
|
4527
|
+
lastSnapshotId = snapshotId2;
|
|
4528
|
+
lastTemplate = template;
|
|
4529
|
+
}
|
|
4530
|
+
return lastTemplate;
|
|
4531
|
+
}
|
|
4434
4532
|
class VmsNamespace {
|
|
4435
4533
|
constructor(apiClient) {
|
|
4436
4534
|
this.apiClient = apiClient;
|
|
@@ -4446,6 +4544,12 @@ class VmsNamespace {
|
|
|
4446
4544
|
const builders = options.with || {};
|
|
4447
4545
|
const { with: _, ...baseConfig } = options;
|
|
4448
4546
|
let config = baseConfig;
|
|
4547
|
+
if (config.template instanceof VmTemplate) {
|
|
4548
|
+
config.template = await ensureNestedTemplates(
|
|
4549
|
+
config.template,
|
|
4550
|
+
this.snapshots
|
|
4551
|
+
);
|
|
4552
|
+
}
|
|
4449
4553
|
const keys = Object.keys(builders);
|
|
4450
4554
|
for (const key of keys) {
|
|
4451
4555
|
const builder = builders[key];
|
|
@@ -4456,7 +4560,7 @@ class VmsNamespace {
|
|
|
4456
4560
|
const response = await this.apiClient.post("/v1/vms", {
|
|
4457
4561
|
body: {
|
|
4458
4562
|
...config,
|
|
4459
|
-
template: config.template instanceof VmTemplate ? config.template["
|
|
4563
|
+
template: config.template instanceof VmTemplate ? config.template["raw"] : config.template
|
|
4460
4564
|
}
|
|
4461
4565
|
});
|
|
4462
4566
|
const vmId = response.id;
|
|
@@ -4502,9 +4606,9 @@ class VmsNamespace {
|
|
|
4502
4606
|
}
|
|
4503
4607
|
}
|
|
4504
4608
|
class VmTemplate {
|
|
4505
|
-
|
|
4609
|
+
raw;
|
|
4506
4610
|
constructor(template) {
|
|
4507
|
-
this.
|
|
4611
|
+
this.raw = template;
|
|
4508
4612
|
}
|
|
4509
4613
|
}
|
|
4510
4614
|
|