freestyle-sandboxes 0.1.39 → 0.1.40

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 CHANGED
@@ -6009,6 +6009,64 @@ function composeVmSpecs(specs) {
6009
6009
  }
6010
6010
 
6011
6011
  const DEFAULT_CONFIGURE_BASE_IMAGE = "FROM debian:trixie-slim";
6012
+ const RUN_COMMANDS_SYSTEMD_SERVICE_PREFIX = "freestyle-run-command";
6013
+ const WAIT_FOR_SYSTEMD_SERVICE_PREFIX = "freestyle-wait-for";
6014
+ function escapeRegExp(value) {
6015
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6016
+ }
6017
+ function getGeneratedServiceState(services, prefix) {
6018
+ const generatedPrefixes = [
6019
+ RUN_COMMANDS_SYSTEMD_SERVICE_PREFIX,
6020
+ WAIT_FOR_SYSTEMD_SERVICE_PREFIX
6021
+ ];
6022
+ let maxIndex = 0;
6023
+ let lastGeneratedServiceName;
6024
+ for (const service of services) {
6025
+ if (!service.name) {
6026
+ continue;
6027
+ }
6028
+ for (const generatedPrefix of generatedPrefixes) {
6029
+ const match = service.name.match(
6030
+ new RegExp(`^${escapeRegExp(generatedPrefix)}-(\\d+)$`)
6031
+ );
6032
+ if (!match) {
6033
+ continue;
6034
+ }
6035
+ if (generatedPrefix === prefix) {
6036
+ const index = Number.parseInt(match[1] ?? "0", 10);
6037
+ if (index > maxIndex) {
6038
+ maxIndex = index;
6039
+ }
6040
+ }
6041
+ lastGeneratedServiceName = service.name;
6042
+ break;
6043
+ }
6044
+ }
6045
+ return { maxIndex, lastGeneratedServiceName };
6046
+ }
6047
+ function appendServiceDependency(dependencies, dependency) {
6048
+ if (!dependency) {
6049
+ return dependencies ?? void 0;
6050
+ }
6051
+ const values = [...dependencies ?? []];
6052
+ if (!values.includes(dependency)) {
6053
+ values.push(dependency);
6054
+ }
6055
+ return values;
6056
+ }
6057
+ function buildWaitForScript(command, intervalSeconds) {
6058
+ const body = command.trim().split("\n").map((line) => ` ${line}`).join("\n");
6059
+ return [
6060
+ "while true; do",
6061
+ " if (",
6062
+ body,
6063
+ " ); then",
6064
+ " exit 0",
6065
+ " fi",
6066
+ ` sleep ${intervalSeconds}`,
6067
+ "done"
6068
+ ].join("\n");
6069
+ }
6012
6070
  function processSystemdServices(services, existingFiles = {}) {
6013
6071
  const additionalFiles = { ...existingFiles };
6014
6072
  const processedServices = [];
@@ -6043,6 +6101,63 @@ ${bash}`;
6043
6101
  }
6044
6102
  return { services: processedServices, additionalFiles };
6045
6103
  }
6104
+ function normalizeSystemdServices(services) {
6105
+ return services.map((service) => ({
6106
+ ...service,
6107
+ after: service.after?.map((s) => s.includes(".") ? s : `${s}.service`),
6108
+ requires: service.requires?.map(
6109
+ (s) => s.includes(".") ? s : `${s}.service`
6110
+ ),
6111
+ wantedBy: service.wantedBy?.map(
6112
+ (s) => s.includes(".") ? s : `${s}.service`
6113
+ )
6114
+ }));
6115
+ }
6116
+ function normalizePatchedServices(services) {
6117
+ return services.map((service) => ({
6118
+ ...service,
6119
+ after: service.after?.map((s) => s.includes(".") ? s : `${s}.service`),
6120
+ requires: service.requires?.map(
6121
+ (s) => s.includes(".") ? s : `${s}.service`
6122
+ ),
6123
+ wantedBy: service.wantedBy?.map(
6124
+ (s) => s.includes(".") ? s : `${s}.service`
6125
+ )
6126
+ }));
6127
+ }
6128
+ function normalizeTemplateForRequest(template) {
6129
+ const normalizedTemplate = {
6130
+ ...template ?? {}
6131
+ };
6132
+ if (normalizedTemplate.template) {
6133
+ normalizedTemplate.template = normalizeTemplateForRequest(
6134
+ normalizedTemplate.template
6135
+ );
6136
+ }
6137
+ if (normalizedTemplate.baseImage) {
6138
+ normalizedTemplate.baseImage = normalizeBaseImage(
6139
+ normalizedTemplate.baseImage
6140
+ )?.toRaw();
6141
+ }
6142
+ normalizedTemplate.git = normalizeGitOptions(normalizedTemplate.git);
6143
+ if (normalizedTemplate.systemd?.services) {
6144
+ const normalizedServices = normalizeSystemdServices(
6145
+ normalizedTemplate.systemd.services
6146
+ );
6147
+ const { services: processedServices, additionalFiles: bashFiles } = processSystemdServices(
6148
+ normalizedServices,
6149
+ normalizedTemplate.additionalFiles ?? {}
6150
+ );
6151
+ normalizedTemplate.systemd.services = processedServices;
6152
+ normalizedTemplate.additionalFiles = bashFiles;
6153
+ }
6154
+ if (normalizedTemplate.systemd?.patchedServices) {
6155
+ normalizedTemplate.systemd.patchedServices = normalizePatchedServices(
6156
+ normalizedTemplate.systemd.patchedServices
6157
+ );
6158
+ }
6159
+ return normalizedTemplate;
6160
+ }
6046
6161
  function normalizeGitOptions(git) {
6047
6162
  if (!git) return git;
6048
6163
  return {
@@ -6238,10 +6353,12 @@ class Vm {
6238
6353
  params: { vm_id: this.vmId },
6239
6354
  body: options
6240
6355
  });
6241
- const vmId = response.id;
6242
6356
  return {
6243
- vmId,
6244
- vm: new Vm({ vmId, freestyle: this._freestyle })
6357
+ forks: response.forks.map((fork) => ({
6358
+ vmId: fork.id,
6359
+ vm: this._freestyle.vms.ref({ vmId: fork.id })
6360
+ // Create a new Vm instance for the forked VM
6361
+ }))
6245
6362
  };
6246
6363
  }
6247
6364
  /**
@@ -6390,10 +6507,14 @@ class VmSpec {
6390
6507
  return { ...this.withDiscriminators };
6391
6508
  }
6392
6509
  mergeRaw(options) {
6510
+ const existingSnapshot = this.raw.snapshot;
6393
6511
  this.raw = composeCreateVmOptions([
6394
6512
  this.raw,
6395
6513
  options
6396
6514
  ]);
6515
+ if (existingSnapshot !== void 0 && this.raw.snapshot === void 0) {
6516
+ this.raw.snapshot = existingSnapshot;
6517
+ }
6397
6518
  return this;
6398
6519
  }
6399
6520
  clearBuilders() {
@@ -6477,9 +6598,74 @@ class VmSpec {
6477
6598
  return this;
6478
6599
  }
6479
6600
  runCommands(...commands) {
6480
- const image = normalizeBaseImage(this.raw.baseImage) ?? new VmBaseImage();
6481
- image.runCommands(...commands);
6482
- this.raw.baseImage = image;
6601
+ const normalizedCommands = commands.map((command) => command.trim()).filter((command) => command.length > 0);
6602
+ if (normalizedCommands.length === 0) {
6603
+ return this;
6604
+ }
6605
+ const existingSystemd = this.raw.systemd ?? {};
6606
+ const existingServices = [...existingSystemd.services ?? []];
6607
+ let { maxIndex: maxRunCommandIndex, lastGeneratedServiceName } = getGeneratedServiceState(
6608
+ existingServices,
6609
+ RUN_COMMANDS_SYSTEMD_SERVICE_PREFIX
6610
+ );
6611
+ for (const command of normalizedCommands) {
6612
+ const nextIndex = ++maxRunCommandIndex;
6613
+ const serviceName = `${RUN_COMMANDS_SYSTEMD_SERVICE_PREFIX}-${nextIndex}`;
6614
+ existingServices.push({
6615
+ name: serviceName,
6616
+ mode: "oneshot",
6617
+ deleteAfterSuccess: true,
6618
+ bash: command,
6619
+ ...lastGeneratedServiceName ? {
6620
+ after: [lastGeneratedServiceName],
6621
+ requires: [lastGeneratedServiceName]
6622
+ } : {}
6623
+ });
6624
+ lastGeneratedServiceName = serviceName;
6625
+ }
6626
+ this.raw.systemd = {
6627
+ ...existingSystemd,
6628
+ services: existingServices
6629
+ };
6630
+ return this;
6631
+ }
6632
+ waitFor(command, config = {}) {
6633
+ const trimmedCommand = command.trim();
6634
+ if (!trimmedCommand) {
6635
+ throw new Error("VmSpec.waitFor requires a non-empty command");
6636
+ }
6637
+ const intervalSeconds = config.intervalSeconds ?? 2;
6638
+ if (!Number.isFinite(intervalSeconds) || intervalSeconds <= 0) {
6639
+ throw new Error("VmSpec.waitFor intervalSeconds must be greater than 0");
6640
+ }
6641
+ const existingSystemd = this.raw.systemd ?? {};
6642
+ const existingServices = [...existingSystemd.services ?? []];
6643
+ const { maxIndex, lastGeneratedServiceName } = getGeneratedServiceState(
6644
+ existingServices,
6645
+ WAIT_FOR_SYSTEMD_SERVICE_PREFIX
6646
+ );
6647
+ const {
6648
+ intervalSeconds: _intervalSeconds,
6649
+ name,
6650
+ after,
6651
+ requires,
6652
+ timeoutSec,
6653
+ ...rest
6654
+ } = config;
6655
+ existingServices.push({
6656
+ ...rest,
6657
+ name: name ?? `${WAIT_FOR_SYSTEMD_SERVICE_PREFIX}-${maxIndex + 1}`,
6658
+ mode: "oneshot",
6659
+ // deleteAfterSuccess: true,
6660
+ timeoutSec: timeoutSec ?? 0,
6661
+ after: appendServiceDependency(after, lastGeneratedServiceName),
6662
+ requires: appendServiceDependency(requires, lastGeneratedServiceName),
6663
+ bash: buildWaitForScript(trimmedCommand, intervalSeconds)
6664
+ });
6665
+ this.raw.systemd = {
6666
+ ...existingSystemd,
6667
+ services: existingServices
6668
+ };
6483
6669
  return this;
6484
6670
  }
6485
6671
  repo(repo, path) {
@@ -6506,7 +6692,7 @@ class VmSpec {
6506
6692
  function isVmSpecLike$1(value) {
6507
6693
  return !!value && typeof value === "object" && "raw" in value && "with" in value;
6508
6694
  }
6509
- function isVmTemplateLike(value) {
6695
+ function isVmTemplateLike$1(value) {
6510
6696
  return !!value && typeof value === "object" && "raw" in value && "with" in value;
6511
6697
  }
6512
6698
  async function convertSpecSnapshotsToTemplates(spec, processBuilders = true) {
@@ -6632,21 +6818,8 @@ class VmsNamespace {
6632
6818
  }
6633
6819
  }
6634
6820
  if (options.systemd?.services) {
6635
- const normalizedServices = options.systemd.services.map(
6636
- (service) => {
6637
- return {
6638
- ...service,
6639
- after: service.after?.map(
6640
- (s) => s.includes(".") ? s : `${s}.service`
6641
- ),
6642
- requires: service.requires?.map(
6643
- (s) => s.includes(".") ? s : `${s}.service`
6644
- ),
6645
- wantedBy: service.wantedBy?.map(
6646
- (s) => s.includes(".") ? s : `${s}.service`
6647
- )
6648
- };
6649
- }
6821
+ const normalizedServices = normalizeSystemdServices(
6822
+ options.systemd.services
6650
6823
  );
6651
6824
  const { services: processedServices, additionalFiles: bashFiles } = processSystemdServices(
6652
6825
  normalizedServices,
@@ -6656,19 +6829,8 @@ class VmsNamespace {
6656
6829
  options.additionalFiles = bashFiles;
6657
6830
  }
6658
6831
  if (options.systemd?.patchedServices) {
6659
- options.systemd.patchedServices = options.systemd.patchedServices.map(
6660
- (service) => {
6661
- service.after = service.after?.map(
6662
- (s) => s.includes(".") ? s : `${s}.service`
6663
- );
6664
- service.requires = service.requires?.map(
6665
- (s) => s.includes(".") ? s : `${s}.service`
6666
- );
6667
- service.wantedBy = service.wantedBy?.map(
6668
- (s) => s.includes(".") ? s : `${s}.service`
6669
- );
6670
- return service;
6671
- }
6832
+ options.systemd.patchedServices = normalizePatchedServices(
6833
+ options.systemd.patchedServices
6672
6834
  );
6673
6835
  }
6674
6836
  if ("snapshot" in options) {
@@ -6701,7 +6863,7 @@ class VmsNamespace {
6701
6863
  const specBuilders = isVmSpecLike$1(
6702
6864
  options.spec
6703
6865
  ) ? collectSpecBuilders(options.spec) : void 0;
6704
- const templateBuilders = isVmTemplateLike(options.template) ? options.template.with : void 0;
6866
+ const templateBuilders = isVmTemplateLike$1(options.template) ? options.template.with : void 0;
6705
6867
  const builders = {
6706
6868
  ...templateBuilders || {},
6707
6869
  ...specBuilders || {},
@@ -6709,10 +6871,10 @@ class VmsNamespace {
6709
6871
  };
6710
6872
  const { with: _, spec: _spec, ...baseConfig } = options;
6711
6873
  let config = baseConfig;
6712
- if (isVmTemplateLike(config.template)) {
6874
+ if (isVmTemplateLike$1(config.template)) {
6713
6875
  config.template = await processTemplateTree(config.template);
6714
6876
  }
6715
- if (isVmTemplateLike(config.template)) {
6877
+ if (isVmTemplateLike$1(config.template)) {
6716
6878
  config.template = await ensureNestedTemplates(
6717
6879
  config.template,
6718
6880
  this.snapshots
@@ -6726,18 +6888,9 @@ class VmsNamespace {
6726
6888
  }
6727
6889
  }
6728
6890
  if (config.systemd?.services) {
6729
- const normalizedServices = config.systemd.services.map((service) => ({
6730
- ...service,
6731
- after: service.after?.map(
6732
- (s) => s.includes(".") ? s : `${s}.service`
6733
- ),
6734
- requires: service.requires?.map(
6735
- (s) => s.includes(".") ? s : `${s}.service`
6736
- ),
6737
- wantedBy: service.wantedBy?.map(
6738
- (s) => s.includes(".") ? s : `${s}.service`
6739
- )
6740
- }));
6891
+ const normalizedServices = normalizeSystemdServices(
6892
+ config.systemd.services
6893
+ );
6741
6894
  const { services: processedServices, additionalFiles: bashFiles } = processSystemdServices(
6742
6895
  normalizedServices,
6743
6896
  config.additionalFiles ?? {}
@@ -6746,28 +6899,20 @@ class VmsNamespace {
6746
6899
  config.additionalFiles = bashFiles;
6747
6900
  }
6748
6901
  if (config.systemd?.patchedServices) {
6749
- config.systemd.patchedServices = config.systemd.patchedServices.map(
6750
- (service) => {
6751
- service.after = service.after?.map(
6752
- (s) => s.includes(".") ? s : `${s}.service`
6753
- );
6754
- service.requires = service.requires?.map(
6755
- (s) => s.includes(".") ? s : `${s}.service`
6756
- );
6757
- service.wantedBy = service.wantedBy?.map(
6758
- (s) => s.includes(".") ? s : `${s}.service`
6759
- );
6760
- return service;
6761
- }
6902
+ config.systemd.patchedServices = normalizePatchedServices(
6903
+ config.systemd.patchedServices
6762
6904
  );
6763
6905
  }
6764
6906
  config.git = normalizeGitOptions(config.git);
6765
6907
  const serializedBaseImage = normalizeBaseImage(config.baseImage)?.toRaw();
6766
- const rawTemplate = isVmTemplateLike(config.template) ? config.template.raw : config.template;
6908
+ const rawTemplate = isVmTemplateLike$1(config.template) ? config.template.raw : config.template;
6767
6909
  const requestTemplate = serializedBaseImage ? {
6768
6910
  ...rawTemplate ?? {},
6769
6911
  baseImage: serializedBaseImage
6770
6912
  } : rawTemplate;
6913
+ const normalizedRequestTemplate = requestTemplate ? normalizeTemplateForRequest(
6914
+ requestTemplate
6915
+ ) : requestTemplate;
6771
6916
  const {
6772
6917
  baseImage: _baseImage,
6773
6918
  template: _template,
@@ -6776,7 +6921,7 @@ class VmsNamespace {
6776
6921
  const response = await this.freestyle._apiClient.post("/v1/vms", {
6777
6922
  body: {
6778
6923
  ...requestConfig,
6779
- template: requestTemplate,
6924
+ template: normalizedRequestTemplate,
6780
6925
  // Cast systemd since we've processed SystemdServiceInput[] to RawSystemdService[]
6781
6926
  systemd: config.systemd,
6782
6927
  // Normalize git options - default config to {}
@@ -6871,6 +7016,35 @@ function enhanceError(e) {
6871
7016
  if (!e.message.includes("create --snapshot")) {
6872
7017
  e.message = `${e.message}. Hint: use \`npx freestyle-sandboxes@latest vm create --snapshot ${e.body.snapshotId} --ssh --delete\` to debug.`;
6873
7018
  }
7019
+ if (e.body.diagnostics) {
7020
+ const d = e.body.diagnostics;
7021
+ const parts = [];
7022
+ if (d.serviceStates.length > 0) {
7023
+ parts.push("Service states:");
7024
+ for (const g of d.serviceStates) {
7025
+ parts.push(
7026
+ ` ${g.activeState}(${g.subState}): ${g.services.join(", ")}`
7027
+ );
7028
+ }
7029
+ }
7030
+ const allLogs = [
7031
+ ...d.failedServiceLogs ?? [],
7032
+ ...d.additionalFailedServiceLogs ?? []
7033
+ ];
7034
+ if (allLogs.length > 0) {
7035
+ parts.push("Failed service logs:");
7036
+ for (const l of allLogs) {
7037
+ parts.push(` --- ${l.unitName} ---`);
7038
+ parts.push(` ${l.log}`);
7039
+ }
7040
+ }
7041
+ if (parts.length > 0) {
7042
+ e.message = `${e.message}
7043
+
7044
+ Diagnostics:
7045
+ ${parts.join("\n")}`;
7046
+ }
7047
+ }
6874
7048
  }
6875
7049
  return e;
6876
7050
  }
@@ -6938,12 +7112,12 @@ class VmSnapshotsNamespace {
6938
7112
  const { spec: _spec, ...rest } = requestOptions;
6939
7113
  requestOptions = rest;
6940
7114
  }
6941
- if (isVmTemplateLike(requestOptions.template)) {
7115
+ if (isVmTemplateLike$1(requestOptions.template)) {
6942
7116
  const processedTemplate = await processTemplateTree(
6943
7117
  requestOptions.template
6944
7118
  );
6945
7119
  requestOptions.template = processedTemplate;
6946
- if (isVmTemplateLike(processedTemplate.raw.template)) {
7120
+ if (isVmTemplateLike$1(processedTemplate.raw.template)) {
6947
7121
  requestOptions.template = await ensureNestedTemplates(
6948
7122
  processedTemplate,
6949
7123
  this
@@ -6958,7 +7132,9 @@ class VmSnapshotsNamespace {
6958
7132
  return this.apiClient.post("/v1/vms/snapshots", {
6959
7133
  body: {
6960
7134
  ...requestOptions,
6961
- template: isVmTemplateLike(requestOptions.template) ? requestOptions.template.raw : requestOptions.template
7135
+ template: normalizeTemplateForRequest(
7136
+ isVmTemplateLike$1(requestOptions.template) ? requestOptions.template.raw : requestOptions.template
7137
+ )
6962
7138
  }
6963
7139
  }).catch((e) => {
6964
7140
  enhanceError(e);
@@ -6978,22 +7154,9 @@ async function processTemplateTree(template) {
6978
7154
  );
6979
7155
  }
6980
7156
  if (template.raw.systemd?.services) {
6981
- const normalizedServices = template.raw.systemd.services.map((service) => ({
6982
- ...service,
6983
- after: service.after?.map((s) => s.includes(".") ? s : `${s}.service`),
6984
- requires: service.requires?.map(
6985
- (s) => s.includes(".") ? s : `${s}.service`
6986
- ),
6987
- wantedBy: service.wantedBy?.map(
6988
- (s) => s.includes(".") ? s : `${s}.service`
6989
- )
6990
- }));
6991
- const { services: processedServices, additionalFiles: bashFiles } = processSystemdServices(
6992
- normalizedServices,
6993
- template.raw.additionalFiles ?? {}
7157
+ template.raw.systemd.services = normalizeSystemdServices(
7158
+ template.raw.systemd.services
6994
7159
  );
6995
- template.raw.systemd.services = processedServices;
6996
- template.raw.additionalFiles = bashFiles;
6997
7160
  }
6998
7161
  for (const key in template.with) {
6999
7162
  const builder = template.with[key];
@@ -7010,7 +7173,7 @@ async function processTemplateTree(template) {
7010
7173
  }
7011
7174
  if (builder.configureNestedTemplate) {
7012
7175
  let nestedTemplate;
7013
- if (isVmTemplateLike(template.raw.template)) {
7176
+ if (isVmTemplateLike$1(template.raw.template)) {
7014
7177
  nestedTemplate = template.raw.template;
7015
7178
  } else {
7016
7179
  nestedTemplate = new VmTemplate({});
@@ -7033,38 +7196,16 @@ async function processTemplateTree(template) {
7033
7196
  );
7034
7197
  }
7035
7198
  if (template.raw.systemd?.services) {
7036
- const normalizedServices = template.raw.systemd.services.map((service) => ({
7037
- ...service,
7038
- after: service.after?.map((s) => s.includes(".") ? s : `${s}.service`),
7039
- requires: service.requires?.map(
7040
- (s) => s.includes(".") ? s : `${s}.service`
7041
- ),
7042
- wantedBy: service.wantedBy?.map(
7043
- (s) => s.includes(".") ? s : `${s}.service`
7044
- )
7045
- }));
7046
- const { services: processedServices, additionalFiles: bashFiles } = processSystemdServices(
7047
- normalizedServices,
7048
- template.raw.additionalFiles ?? {}
7199
+ template.raw.systemd.services = normalizeSystemdServices(
7200
+ template.raw.systemd.services
7049
7201
  );
7050
- template.raw.systemd.services = processedServices;
7051
- template.raw.additionalFiles = bashFiles;
7052
7202
  }
7053
7203
  if (template.raw.systemd?.patchedServices) {
7054
- template.raw.systemd.patchedServices = template.raw.systemd.patchedServices.map((service) => ({
7055
- ...service,
7056
- after: service.after?.map(
7057
- (s) => s.includes(".") ? s : `${s}.service`
7058
- ),
7059
- requires: service.requires?.map(
7060
- (s) => s.includes(".") ? s : `${s}.service`
7061
- ),
7062
- wantedBy: service.wantedBy?.map(
7063
- (s) => s.includes(".") ? s : `${s}.service`
7064
- )
7065
- }));
7204
+ template.raw.systemd.patchedServices = normalizePatchedServices(
7205
+ template.raw.systemd.patchedServices
7206
+ );
7066
7207
  }
7067
- if (isVmTemplateLike(template.raw.template)) {
7208
+ if (isVmTemplateLike$1(template.raw.template)) {
7068
7209
  template.raw.template = await processTemplateTree(
7069
7210
  template.raw.template
7070
7211
  );
@@ -7073,7 +7214,7 @@ async function processTemplateTree(template) {
7073
7214
  }
7074
7215
  async function ensureNestedTemplates(template, snapshots) {
7075
7216
  const templates = [template];
7076
- while (isVmTemplateLike(templates.at(-1)?.raw.template)) {
7217
+ while (isVmTemplateLike$1(templates.at(-1)?.raw.template)) {
7077
7218
  const innerTemplate = templates.at(-1).raw.template;
7078
7219
  templates.at(-1).raw.template = void 0;
7079
7220
  templates.push(innerTemplate);
@@ -7258,7 +7399,7 @@ async function readFiles(directory) {
7258
7399
  }
7259
7400
 
7260
7401
  async function debugCreateRequests(freestyle, optionsOrSpec = {}) {
7261
- const options = isVmSpecLike(optionsOrSpec) ? { spec: optionsOrSpec } : optionsOrSpec;
7402
+ const options = isVmSpecLike(optionsOrSpec) ? { spec: cloneVmSpec(optionsOrSpec) } : cloneCreateOptions(optionsOrSpec);
7262
7403
  const requests = [];
7263
7404
  let snapshotCounter = 0;
7264
7405
  let vmCounter = 0;
@@ -7305,6 +7446,79 @@ async function debugCreateRequests(freestyle, optionsOrSpec = {}) {
7305
7446
  function isVmSpecLike(value) {
7306
7447
  return !!value && typeof value === "object" && "raw" in value && "with" in value;
7307
7448
  }
7449
+ function isVmTemplateLike(value) {
7450
+ return !!value && typeof value === "object" && "raw" in value && "with" in value;
7451
+ }
7452
+ function cloneCreateOptions(options) {
7453
+ return {
7454
+ ...clonePlainObject(options),
7455
+ with: options.with,
7456
+ template: isVmTemplateLike(options.template) ? cloneVmTemplate(options.template) : cloneValue(options.template),
7457
+ spec: isVmSpecLike(options.spec) ? cloneVmSpec(options.spec) : void 0,
7458
+ snapshot: isVmSpecLike(options.snapshot) ? cloneVmSpec(options.snapshot) : void 0
7459
+ };
7460
+ }
7461
+ function cloneVmSpec(spec) {
7462
+ const clonedRaw = cloneVmSpecRaw(spec.raw);
7463
+ return new VmSpec({
7464
+ ...clonedRaw,
7465
+ with: { ...spec.builders },
7466
+ __withDiscriminators: typeof spec.getBuilderDiscriminators === "function" ? spec.getBuilderDiscriminators() : void 0
7467
+ });
7468
+ }
7469
+ function cloneVmTemplate(template) {
7470
+ const clonedRaw = cloneVmTemplateRaw(template.raw);
7471
+ return new VmTemplate({
7472
+ ...clonedRaw,
7473
+ with: { ...template.with }
7474
+ });
7475
+ }
7476
+ function cloneVmSpecRaw(raw) {
7477
+ const cloned = cloneVmTemplateRaw(raw);
7478
+ if (isVmSpecLike(raw.snapshot)) {
7479
+ cloned.snapshot = cloneVmSpec(raw.snapshot);
7480
+ } else if (raw.snapshot !== void 0) {
7481
+ cloned.snapshot = cloneValue(raw.snapshot);
7482
+ }
7483
+ return cloned;
7484
+ }
7485
+ function cloneVmTemplateRaw(raw) {
7486
+ const cloned = clonePlainObject(raw);
7487
+ if (isVmTemplateLike(raw.template)) {
7488
+ cloned.template = cloneVmTemplate(raw.template);
7489
+ } else if (raw.template !== void 0) {
7490
+ cloned.template = cloneValue(raw.template);
7491
+ }
7492
+ return cloned;
7493
+ }
7494
+ function clonePlainObject(value) {
7495
+ if (!value || typeof value !== "object") {
7496
+ return value;
7497
+ }
7498
+ const cloned = {};
7499
+ for (const [key, entry] of Object.entries(value)) {
7500
+ cloned[key] = cloneValue(entry);
7501
+ }
7502
+ return cloned;
7503
+ }
7504
+ function cloneValue(value) {
7505
+ if (value instanceof VmBaseImage) {
7506
+ return new VmBaseImage(value.toRaw());
7507
+ }
7508
+ if (isVmSpecLike(value)) {
7509
+ return cloneVmSpec(value);
7510
+ }
7511
+ if (isVmTemplateLike(value)) {
7512
+ return cloneVmTemplate(value);
7513
+ }
7514
+ if (Array.isArray(value)) {
7515
+ return value.map((entry) => cloneValue(entry));
7516
+ }
7517
+ if (value && typeof value === "object") {
7518
+ return clonePlainObject(value);
7519
+ }
7520
+ return value;
7521
+ }
7308
7522
 
7309
7523
  class Freestyle {
7310
7524
  /**