@ornncompute/cli 1.0.75 → 1.0.97

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.
Files changed (52) hide show
  1. package/README.md +32 -5
  2. package/package.json +4 -2
  3. package/src/admin-capacity-reservations.mjs +4 -1
  4. package/src/admin-command-handlers.mjs +2 -0
  5. package/src/admin-node-diagnostics.mjs +1 -5
  6. package/src/admin-node-lifecycle.mjs +23 -125
  7. package/src/admin-node-sanitize.mjs +243 -0
  8. package/src/catalog-shell.mjs +6 -6
  9. package/src/evidence-commands.mjs +4 -6
  10. package/src/external-bucket-commands.mjs +71 -23
  11. package/src/interaction-runtime.mjs +3 -0
  12. package/src/kubernetes-commands.mjs +16 -83
  13. package/src/machine-reference.mjs +33 -56
  14. package/src/slurm-commands.mjs +13 -2
  15. package/src/vm-commands.mjs +39 -59
  16. package/vendor/api-client/commerce.js +743 -0
  17. package/vendor/capabilities/cli.js +7688 -7
  18. package/vendor/capabilities/capabilities/admin-capacity-reservations.js +0 -101
  19. package/vendor/capabilities/capabilities/admin-enrollment.js +0 -322
  20. package/vendor/capabilities/capabilities/admin-node-diagnostics.js +0 -88
  21. package/vendor/capabilities/capabilities/admin-nodes-lifecycle.js +0 -137
  22. package/vendor/capabilities/capabilities/admin.js +0 -91
  23. package/vendor/capabilities/capabilities/cli.js +0 -156
  24. package/vendor/capabilities/capabilities/clusters.js +0 -248
  25. package/vendor/capabilities/capabilities/commerce-market.js +0 -405
  26. package/vendor/capabilities/capabilities/compute.js +0 -523
  27. package/vendor/capabilities/capabilities/credits.js +0 -83
  28. package/vendor/capabilities/capabilities/evidence.js +0 -145
  29. package/vendor/capabilities/capabilities/external-buckets.js +0 -191
  30. package/vendor/capabilities/capabilities/fleet.js +0 -386
  31. package/vendor/capabilities/capabilities/identity.js +0 -129
  32. package/vendor/capabilities/capabilities/listings.js +0 -398
  33. package/vendor/capabilities/capabilities/locality.js +0 -42
  34. package/vendor/capabilities/capabilities/market-shared.js +0 -46
  35. package/vendor/capabilities/capabilities/markets.js +0 -482
  36. package/vendor/capabilities/capabilities/mounts.js +0 -176
  37. package/vendor/capabilities/capabilities/native-listing.js +0 -99
  38. package/vendor/capabilities/capabilities/networks.js +0 -1264
  39. package/vendor/capabilities/capabilities/observability.js +0 -253
  40. package/vendor/capabilities/capabilities/organization-invites.js +0 -53
  41. package/vendor/capabilities/capabilities/reservation-list.js +0 -68
  42. package/vendor/capabilities/capabilities/staff-shared.js +0 -64
  43. package/vendor/capabilities/capabilities/tokens.js +0 -161
  44. package/vendor/capabilities/capabilities/users.js +0 -157
  45. package/vendor/capabilities/capabilities/volumes.js +0 -178
  46. package/vendor/capabilities/capability.js +0 -99
  47. package/vendor/capabilities/cli-catalog.js +0 -73
  48. package/vendor/capabilities/cli-migrations.js +0 -264
  49. package/vendor/capabilities/names.js +0 -6
  50. package/vendor/capabilities/software-catalog.js +0 -82
  51. package/vendor/capabilities/spec-catalog.js +0 -151
  52. package/vendor/capabilities/supplier-directory.js +0 -20
@@ -1,3 +1,4 @@
1
+ import { getReservation } from "../vendor/api-client/commerce.js";
1
2
  import { spawn as spawnProcessDefault } from "node:child_process";
2
3
  import { createHash } from "node:crypto";
3
4
  import { readFile } from "node:fs/promises";
@@ -9,6 +10,7 @@ import { resolvedMountPath } from "./mount-commands.mjs";
9
10
  import {
10
11
  isReferenceMiss,
11
12
  listActiveDirectReservations,
13
+ listActiveReservations,
12
14
  listOwnedActiveMachines,
13
15
  reservationLabeler,
14
16
  resolveMachineReference,
@@ -70,14 +72,13 @@ function machineAccessMode(value) {
70
72
 
71
73
  function reservationAccessMode(value) {
72
74
  const reservation = record(value);
73
- const mode =
74
- (text(reservation.access_mode) ?? text(reservation.accessMode))?.toLowerCase() ?? null;
75
+ const mode = text(reservation.provisioningType)?.toLowerCase() ?? null;
75
76
  return mode === "virtual-machine" ? "vm" : mode;
76
77
  }
77
78
 
78
79
  function isDirectReservation(value) {
79
80
  const mode = reservationAccessMode(value);
80
- return !mode || mode === "vm" || mode === "bare-metal";
81
+ return mode === "vm" || mode === "bare-metal";
81
82
  }
82
83
 
83
84
  function machineIsReady(value) {
@@ -424,7 +425,7 @@ async function selectedOrganization(context, interaction, expectedId) {
424
425
 
425
426
  function assertActiveReservation(value, expectedRef, interaction, expectedMode = null) {
426
427
  const reservation = record(value);
427
- const ref = resourceId(reservation);
428
+ const ref = text(reservation.reservationId);
428
429
  if (!ref || ref !== expectedRef) {
429
430
  interaction.error("The gateway returned an unrelated capacity reservation.", {
430
431
  code: "reservation_correlation_changed",
@@ -463,7 +464,7 @@ async function resolveReservation(context, input, interaction, expectedMode = nu
463
464
  if (supplied) {
464
465
  const ref = requireCanonical(interaction, supplied, "reservation", "Capacity reservation");
465
466
  return assertActiveReservation(
466
- await context.fetch(`/v1/workflows/commerce/reservations/${encodeURIComponent(ref)}`),
467
+ await getReservation(context.fetch, ref),
467
468
  ref,
468
469
  interaction,
469
470
  expectedMode
@@ -475,29 +476,23 @@ async function resolveReservation(context, input, interaction, expectedMode = nu
475
476
  field: "reservation",
476
477
  });
477
478
  }
478
- const payload = await context.fetch("/v1/workflows/commerce/reservations?status=active&limit=50");
479
479
  const requestedMode = expectedMode ?? text(input.mode)?.toLowerCase() ?? null;
480
- const candidates = (Array.isArray(payload) ? payload : (record(payload).reservations ?? []))
481
- .map(record)
482
- .filter(
483
- (reservation) =>
484
- (text(reservation.status) ?? "").toLowerCase() === ACTIVE_RESERVATION &&
485
- resourceId(reservation) &&
486
- (!requestedMode || reservationAccessMode(reservation) === requestedMode)
487
- );
480
+ const candidates = (await listActiveReservations(context, interaction)).filter(
481
+ (reservation) => !requestedMode || reservationAccessMode(reservation) === requestedMode
482
+ );
488
483
  if (!candidates.length) {
489
484
  interaction.error("No active capacity reservation is eligible for launch.", {
490
485
  code: "no_active_reservation",
491
486
  });
492
487
  }
493
- let ref = resourceId(candidates[0]);
488
+ let ref = text(candidates[0].reservationId);
494
489
  if (candidates.length > 1) {
495
490
  const reservationLabel = await reservationLabeler(context.fetch);
496
491
  ref = await interaction.ask({
497
492
  label: "Capacity reservation",
498
493
  choices: candidates.map((reservation) => ({
499
494
  label: reservationLabel(reservation),
500
- value: resourceId(reservation),
495
+ value: text(reservation.reservationId),
501
496
  })),
502
497
  });
503
498
  }
@@ -506,7 +501,7 @@ async function resolveReservation(context, input, interaction, expectedMode = nu
506
501
  code: "missing_input",
507
502
  });
508
503
  return assertActiveReservation(
509
- await context.fetch(`/v1/workflows/commerce/reservations/${encodeURIComponent(ref)}`),
504
+ await getReservation(context.fetch, ref),
510
505
  ref,
511
506
  interaction,
512
507
  expectedMode
@@ -1044,7 +1039,7 @@ export const vmLaunchModeHandler = {
1044
1039
  input = await normalizePublicKeyInput(input);
1045
1040
  const organization = await selectedOrganization(context, interaction);
1046
1041
  const reservation = await resolveReservation(context, input, interaction, "vm");
1047
- const reservationId = resourceId(reservation);
1042
+ const reservationId = text(reservation.reservationId);
1048
1043
  const keyPlan = await keyPlanFromInput(context, input, interaction);
1049
1044
  const [reviewedMachines, reviewedRelationships] = await Promise.all([
1050
1045
  fetchMachines(context, reservationId),
@@ -1095,9 +1090,7 @@ export const vmLaunchModeHandler = {
1095
1090
 
1096
1091
  await selectedOrganization(context, interaction, organization.id);
1097
1092
  const currentReservation = assertActiveReservation(
1098
- await context.fetch(
1099
- `/v1/workflows/commerce/reservations/${encodeURIComponent(reservationId)}`
1100
- ),
1093
+ await getReservation(context.fetch, reservationId),
1101
1094
  reservationId,
1102
1095
  interaction,
1103
1096
  "vm"
@@ -1348,15 +1341,13 @@ function reservationAllocationContext(value) {
1348
1341
  const reservation = record(value);
1349
1342
  return {
1350
1343
  term: {
1351
- start: text(reservation.start_at) ?? text(reservation.start),
1352
- end: text(reservation.end_at) ?? text(reservation.end),
1344
+ start: text(reservation.startsAt),
1345
+ end: text(reservation.endsAt),
1353
1346
  },
1354
- machine_count: Number.isSafeInteger(reservation.node_count)
1355
- ? reservation.node_count
1356
- : Number.isSafeInteger(reservation.machine_count)
1357
- ? reservation.machine_count
1358
- : null,
1359
- gpu_count: Number.isSafeInteger(reservation.gpu_count) ? reservation.gpu_count : null,
1347
+ machine_count: reservation.reservedNodeCount,
1348
+ gpu_count: Number.isSafeInteger(reservation.reservedGpuCount)
1349
+ ? reservation.reservedGpuCount
1350
+ : null,
1360
1351
  };
1361
1352
  }
1362
1353
 
@@ -1420,7 +1411,7 @@ function bareMetalIntentSignature(reservation, relationships) {
1420
1411
  }
1421
1412
 
1422
1413
  function bareMetalLaunchData(reservation, machine, key, relationships, waitPolicy, replayed) {
1423
- const reservationId = resourceId(reservation);
1414
+ const reservationId = text(reservation.reservationId);
1424
1415
  const invocation = isSshReady(machine, key) ? sshInvocation(machine) : null;
1425
1416
  return {
1426
1417
  mode: "bare-metal",
@@ -1506,16 +1497,13 @@ function durableWorkspaceAllocation(reservation, interaction) {
1506
1497
  const endMilliseconds = Date.parse(end ?? "");
1507
1498
  const duration = endMilliseconds - startMilliseconds;
1508
1499
  const machineCount = allocation.machine_count;
1509
- const gpusPerMachine = Number.isSafeInteger(reservation.gpus_per_node)
1510
- ? reservation.gpus_per_node
1511
- : Number.isSafeInteger(reservation.gpus_per_machine)
1512
- ? reservation.gpus_per_machine
1513
- : null;
1500
+ const gpusPerMachine = reservation.gpusPerNode;
1514
1501
  const gpuCount = Number.isSafeInteger(allocation.gpu_count)
1515
1502
  ? allocation.gpu_count
1516
1503
  : Number.isSafeInteger(machineCount) && Number.isSafeInteger(gpusPerMachine)
1517
1504
  ? machineCount * gpusPerMachine
1518
1505
  : null;
1506
+ // CPU capacity has no GPUs; a workspace needs only its whole machines.
1519
1507
  if (
1520
1508
  !start ||
1521
1509
  !end ||
@@ -1523,9 +1511,7 @@ function durableWorkspaceAllocation(reservation, interaction) {
1523
1511
  !Number.isFinite(endMilliseconds) ||
1524
1512
  duration <= 0 ||
1525
1513
  !Number.isSafeInteger(machineCount) ||
1526
- machineCount < 1 ||
1527
- !Number.isSafeInteger(gpuCount) ||
1528
- gpuCount < 1
1514
+ machineCount < 1
1529
1515
  ) {
1530
1516
  interaction.error("The active capacity reservation does not contain valid allocation facts.", {
1531
1517
  code: "reservation_allocation_incomplete",
@@ -1627,7 +1613,7 @@ function workspaceRelationshipConflict(facts, goal, interaction) {
1627
1613
  function durableWorkspaceSignature(reservation, key, facts, goal, interaction) {
1628
1614
  return JSON.stringify({
1629
1615
  capacity_reservation: {
1630
- id: resourceId(reservation),
1616
+ id: text(reservation.reservationId),
1631
1617
  status: text(reservation.status),
1632
1618
  allocation: durableWorkspaceAllocation(reservation, interaction),
1633
1619
  },
@@ -1784,7 +1770,7 @@ async function launchDurableBareMetalWorkspace({
1784
1770
  keyPlan,
1785
1771
  }) {
1786
1772
  const id = workspaceId(input, interaction);
1787
- const reservationId = resourceId(reservation);
1773
+ const reservationId = text(reservation.reservationId);
1788
1774
  if (keyPlan.kind !== "existing") {
1789
1775
  interaction.usage("A durable Bare Metal workspace requires a declared existing SSH key.", {
1790
1776
  code: "workspace_ssh_key_required",
@@ -1828,8 +1814,8 @@ async function launchDurableBareMetalWorkspace({
1828
1814
  value: `${allocation.term.start} to ${allocation.term.end} (${allocation.term.hours} hours)`,
1829
1815
  },
1830
1816
  {
1831
- label: "GPU quantity",
1832
- value: `${allocation.quantity.gpu_count} GPUs across ${allocation.quantity.machine_count} machine${allocation.quantity.machine_count === 1 ? "" : "s"}`,
1817
+ label: "Quantity",
1818
+ value: `${allocation.quantity.gpu_count > 0 ? `${allocation.quantity.gpu_count} GPUs` : "CPU"} across ${allocation.quantity.machine_count} machine${allocation.quantity.machine_count === 1 ? "" : "s"}`,
1833
1819
  },
1834
1820
  {
1835
1821
  label: "Creation",
@@ -1860,7 +1846,7 @@ async function launchDurableBareMetalWorkspace({
1860
1846
 
1861
1847
  await selectedOrganization(context, interaction, organization.id);
1862
1848
  const [currentReservationValue, currentKeyPlan, currentFacts] = await Promise.all([
1863
- context.fetch(`/v1/workflows/commerce/reservations/${encodeURIComponent(reservationId)}`),
1849
+ getReservation(context.fetch, reservationId),
1864
1850
  keyPlanFromInput(context, input, interaction),
1865
1851
  workspaceResourceFacts(context, interaction, input, reservationId),
1866
1852
  ]);
@@ -2290,7 +2276,7 @@ export const bareMetalLaunchModeHandler = {
2290
2276
  input = await normalizePublicKeyInput(input);
2291
2277
  const organization = await selectedOrganization(context, interaction, resumeOrganizationId);
2292
2278
  const reservation = await resolveReservation(context, input, interaction, "bare-metal");
2293
- const reservationId = resourceId(reservation);
2279
+ const reservationId = text(reservation.reservationId);
2294
2280
  const keyPlan = await keyPlanFromInput(context, input, interaction);
2295
2281
  if (durableWorkspace) {
2296
2282
  return launchDurableBareMetalWorkspace({
@@ -2358,7 +2344,7 @@ export const bareMetalLaunchModeHandler = {
2358
2344
 
2359
2345
  const [currentReservationValue, currentRelationships, existingAssigned, projectedMachines] =
2360
2346
  await Promise.all([
2361
- context.fetch(`/v1/workflows/commerce/reservations/${encodeURIComponent(reservationId)}`),
2347
+ getReservation(context.fetch, reservationId),
2362
2348
  bareMetalRelationships(context, reservationId),
2363
2349
  fetchAssignedKeys(context, reservationId),
2364
2350
  fetchMachines(context, reservationId),
@@ -2740,9 +2726,7 @@ function createDirectMachineConnectModeHandler({
2740
2726
  code: "machine_correlation_failed",
2741
2727
  });
2742
2728
  }
2743
- const reservation = await context.fetch(
2744
- `/v1/workflows/commerce/reservations/${encodeURIComponent(reservationId)}`
2745
- );
2729
+ const reservation = await getReservation(context.fetch, reservationId);
2746
2730
  if (!isDirectReservation(reservation)) return null;
2747
2731
  const privateTransportRequired = text(machine.network_mode) === "private";
2748
2732
  return {
@@ -3074,9 +3058,7 @@ async function resolveMachineCommandTarget(input, context, interaction, usage) {
3074
3058
  code: "machine_correlation_failed",
3075
3059
  });
3076
3060
  }
3077
- const reservation = await context.fetch(
3078
- `/v1/workflows/commerce/reservations/${encodeURIComponent(reservationId)}`
3079
- );
3061
+ const reservation = await getReservation(context.fetch, reservationId);
3080
3062
  if (!isDirectReservation(reservation)) {
3081
3063
  interaction.error(
3082
3064
  "The selected machine belongs to a cluster rather than the direct-machine lifecycle.",
@@ -3331,19 +3313,19 @@ async function guidedSwitchReservation(context, interaction) {
3331
3313
  code: "no_active_reservation",
3332
3314
  });
3333
3315
  }
3334
- let reference = resourceId(reservations[0]);
3316
+ let reference = text(reservations[0].reservationId);
3335
3317
  if (reservations.length > 1) {
3336
3318
  const reservationLabel = await reservationLabeler(context.fetch);
3337
3319
  reference = await interaction.ask({
3338
3320
  label: "Capacity reservation",
3339
3321
  choices: reservations.map((reservation) => ({
3340
3322
  label: reservationLabel(reservation),
3341
- value: resourceId(reservation),
3323
+ value: text(reservation.reservationId),
3342
3324
  })),
3343
3325
  });
3344
3326
  }
3345
3327
  return assertActiveReservation(
3346
- await context.fetch(`/v1/workflows/commerce/reservations/${encodeURIComponent(reference)}`),
3328
+ await getReservation(context.fetch, reference),
3347
3329
  reference,
3348
3330
  interaction
3349
3331
  );
@@ -3380,14 +3362,12 @@ async function switchMachine(input, context, interaction) {
3380
3362
  }
3381
3363
  const reservation = suppliedReservation
3382
3364
  ? assertActiveReservation(
3383
- await context.fetch(
3384
- `/v1/workflows/commerce/reservations/${encodeURIComponent(suppliedReservation)}`
3385
- ),
3365
+ await getReservation(context.fetch, suppliedReservation),
3386
3366
  suppliedReservation,
3387
3367
  interaction
3388
3368
  )
3389
3369
  : await guidedSwitchReservation(context, interaction);
3390
- const reservationId = resourceId(reservation);
3370
+ const reservationId = text(reservation.reservationId);
3391
3371
  if (!isDirectReservation(reservation)) {
3392
3372
  interaction.error("machines switch accepts only a direct VM or Bare Metal reservation.", {
3393
3373
  code: "reservation_not_direct",
@@ -3496,7 +3476,7 @@ async function switchMachine(input, context, interaction) {
3496
3476
  }
3497
3477
  const [currentReservationValue, currentMachines, currentRelationships, currentKeys] =
3498
3478
  await Promise.all([
3499
- context.fetch(`/v1/workflows/commerce/reservations/${encodeURIComponent(reservationId)}`),
3479
+ getReservation(context.fetch, reservationId),
3500
3480
  fetchMachines(context, reservationId),
3501
3481
  bareMetalRelationships(context, reservationId),
3502
3482
  fetchAssignedKeys(context, reservationId),