@ornncompute/cli 1.0.116 → 1.0.117
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/package.json +1 -1
- package/src/api-client.mjs +3 -3
- package/src/evidence-commands.mjs +22 -20
- package/src/external-bucket-commands.mjs +16 -20
- package/src/kubernetes-commands.mjs +52 -52
- package/src/mount-commands.mjs +19 -30
- package/src/vm-commands.mjs +21 -25
- package/vendor/api-client/gateway.js +363 -0
- package/vendor/capabilities/cli.js +173 -94
package/package.json
CHANGED
package/src/api-client.mjs
CHANGED
|
@@ -251,9 +251,9 @@ export function resolveInstallerBaseUrl({ env = process.env } = {}) {
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
function operatorProxyEndpoint(endpoint) {
|
|
254
|
-
//
|
|
255
|
-
// envelope, including its admin-role and impersonation checks.
|
|
256
|
-
const path = normalizeComputePath(endpoint).replace(/^\/v1\/benchmark(?=\/|$)/, "");
|
|
254
|
+
// Shared clients use canonical staff or Benchmark paths. Keep the existing
|
|
255
|
+
// staff envelope, including its admin-role and impersonation checks.
|
|
256
|
+
const path = normalizeComputePath(endpoint).replace(/^\/v1\/(?:staff|benchmark)(?=\/|$)/, "");
|
|
257
257
|
return `/v1/staff${path}`;
|
|
258
258
|
}
|
|
259
259
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as gatewayApi from "../vendor/api-client/gateway.js";
|
|
1
2
|
import { gpuLabeler } from "./gpu-catalog.mjs";
|
|
2
3
|
import { clusterFactLabel, reservationFactsForCluster } from "./kubernetes-commands.mjs";
|
|
3
4
|
import { listOwnedActiveMachines, resolveMachineReference } from "./machine-reference.mjs";
|
|
@@ -410,7 +411,7 @@ async function activeMachines(fetch, interaction) {
|
|
|
410
411
|
}
|
|
411
412
|
|
|
412
413
|
async function clusters(fetch, interaction) {
|
|
413
|
-
const payload = await fetch
|
|
414
|
+
const payload = await gatewayApi.listClusters(fetch);
|
|
414
415
|
const rows = payloadRows(payload, "clusters");
|
|
415
416
|
if (!rows)
|
|
416
417
|
interaction.error("The gateway returned an invalid cluster collection.", {
|
|
@@ -769,23 +770,22 @@ function invalidEvidenceResponse(code) {
|
|
|
769
770
|
return error;
|
|
770
771
|
}
|
|
771
772
|
|
|
772
|
-
function query(params) {
|
|
773
|
-
return new URLSearchParams(
|
|
774
|
-
Object.entries(params).filter(([, value]) => value !== null && value !== undefined)
|
|
775
|
-
).toString();
|
|
776
|
-
}
|
|
777
|
-
|
|
778
773
|
async function metricSource(machine, reservation, window, maximum, deps, deadline, signal, live) {
|
|
779
|
-
const path = live ? "live-metrics" : "telemetry-history";
|
|
780
|
-
const params = live
|
|
781
|
-
? { machine_id: machine.id }
|
|
782
|
-
: { machine_id: machine.id, start: window.start, end: window.end, max_points: String(maximum) };
|
|
783
774
|
const response = await fetchEvidence(
|
|
784
775
|
async (requestSignal) => {
|
|
785
|
-
const payload =
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
776
|
+
const payload = live
|
|
777
|
+
? await gatewayApi.getReservationLiveMetrics(
|
|
778
|
+
deps.fetch,
|
|
779
|
+
reservation,
|
|
780
|
+
{ machine_id: machine.id },
|
|
781
|
+
requestSignal
|
|
782
|
+
)
|
|
783
|
+
: await gatewayApi.getReservationTelemetryHistory(
|
|
784
|
+
deps.fetch,
|
|
785
|
+
reservation,
|
|
786
|
+
{ machine_id: machine.id, start: window.start, end: window.end, max_points: maximum },
|
|
787
|
+
requestSignal
|
|
788
|
+
);
|
|
789
789
|
const points = metricPoints(payload, maximum, window);
|
|
790
790
|
if (points === null) throw invalidEvidenceResponse("invalid_metrics_response");
|
|
791
791
|
return points;
|
|
@@ -830,16 +830,18 @@ async function logSource(
|
|
|
830
830
|
) {
|
|
831
831
|
const response = await fetchEvidence(
|
|
832
832
|
async (requestSignal) => {
|
|
833
|
-
const payload = await
|
|
834
|
-
|
|
833
|
+
const payload = await gatewayApi.getReservationLogs(
|
|
834
|
+
deps.fetch,
|
|
835
|
+
reservation,
|
|
836
|
+
{
|
|
835
837
|
machine_id: machine.id,
|
|
836
838
|
stream,
|
|
837
839
|
start: window.start,
|
|
838
840
|
end: window.end,
|
|
839
|
-
limit
|
|
841
|
+
limit,
|
|
840
842
|
cursor,
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
+
},
|
|
844
|
+
requestSignal
|
|
843
845
|
);
|
|
844
846
|
const entries = logLines(payload, limit, window);
|
|
845
847
|
if (entries === null) throw invalidEvidenceResponse("invalid_logs_response");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as gatewayApi from "../vendor/api-client/gateway.js";
|
|
1
2
|
import { createHash } from "node:crypto";
|
|
2
3
|
import { readFile } from "node:fs/promises";
|
|
3
4
|
|
|
@@ -84,7 +85,8 @@ function verificationCode(source) {
|
|
|
84
85
|
return typeof code === "string" && /^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(code) ? code : null;
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
const TRANSFER_ACCOUNT_PATTERN =
|
|
88
|
+
const TRANSFER_ACCOUNT_PATTERN =
|
|
89
|
+
/^[a-z][a-z0-9-]{4,28}[a-z0-9]@[a-z][a-z0-9-]{4,28}[a-z0-9]\.iam\.gserviceaccount\.com$/;
|
|
88
90
|
|
|
89
91
|
function safeTransferAccount(value) {
|
|
90
92
|
return typeof value === "string" && TRANSFER_ACCOUNT_PATTERN.test(value) ? value : null;
|
|
@@ -767,11 +769,10 @@ function attachmentFrom(payload) {
|
|
|
767
769
|
|
|
768
770
|
async function fetchImportAttachment(runtime, reservationId, { signal } = {}) {
|
|
769
771
|
try {
|
|
770
|
-
const payload = await
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
{ method: "GET", ...(signal ? { signal } : {}) }
|
|
772
|
+
const payload = await gatewayApi.getReservationStorageAttachment(
|
|
773
|
+
runtime.fetch,
|
|
774
|
+
reservationId,
|
|
775
|
+
signal
|
|
775
776
|
);
|
|
776
777
|
return attachmentFrom(payload);
|
|
777
778
|
} catch (error) {
|
|
@@ -1217,21 +1218,16 @@ async function importBucket(input, runtime) {
|
|
|
1217
1218
|
const replayed = attachment !== null;
|
|
1218
1219
|
if (!attachment) {
|
|
1219
1220
|
try {
|
|
1220
|
-
const payload = await
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
),
|
|
1221
|
+
const payload = await gatewayApi.deployReservationStorage(
|
|
1222
|
+
runtime.fetch,
|
|
1223
|
+
reservationId,
|
|
1224
1224
|
{
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
headers: idempotencyHeaders(input),
|
|
1232
|
-
method: "POST",
|
|
1233
|
-
signal: runtime.signal,
|
|
1234
|
-
}
|
|
1225
|
+
drive_id: bucket.id,
|
|
1226
|
+
mount_path: "/home/tenant/volume",
|
|
1227
|
+
mount_mode: "fuse",
|
|
1228
|
+
access_mode: "read-write",
|
|
1229
|
+
},
|
|
1230
|
+
{ headers: idempotencyHeaders(input), signal: runtime.signal }
|
|
1235
1231
|
);
|
|
1236
1232
|
attachment = attachmentFrom(payload);
|
|
1237
1233
|
if (!attachment || String(attachment.drive_id ?? "") !== bucket.id) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as gatewayApi from "../vendor/api-client/gateway.js";
|
|
1
2
|
import { getReservation } from "../vendor/api-client/commerce.js";
|
|
2
3
|
import { createHash } from "node:crypto";
|
|
3
4
|
|
|
@@ -13,7 +14,6 @@ import { listActiveReservations, reservationLabeler } from "./machine-reference.
|
|
|
13
14
|
import { keyInputShapePreflight, keyPlanFromInput, materializeKey } from "./vm-commands.mjs";
|
|
14
15
|
|
|
15
16
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
16
|
-
const CLUSTERS_PATH = "/v1/workflows/compute/clusters";
|
|
17
17
|
const DEFAULT_LIMIT = 50;
|
|
18
18
|
const MAX_LIMIT = 500;
|
|
19
19
|
const DEFAULT_WAIT_SECONDS = 600;
|
|
@@ -135,17 +135,8 @@ function clusterLines(cluster) {
|
|
|
135
135
|
];
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
function eligibilityPath(reservationId, accessMode = "kubernetes") {
|
|
139
|
-
const query = new URLSearchParams({ access_mode: accessMode });
|
|
140
|
-
return `/v1/workflows/compute/tenants/me/reservations/${encodeURIComponent(reservationId)}/cluster-eligible-machines?${query}`;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function clusterPath(cluster, suffix) {
|
|
144
|
-
return `/v1/workflows/compute/${cluster.access_mode}/reservations/${encodeURIComponent(cluster.reservation_id)}/${suffix}`;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
138
|
async function fetchClusters(fetch) {
|
|
148
|
-
const payload = await fetch
|
|
139
|
+
const payload = await gatewayApi.listClusters(fetch);
|
|
149
140
|
const rows = Array.isArray(payload)
|
|
150
141
|
? payload
|
|
151
142
|
: Array.isArray(record(payload).clusters)
|
|
@@ -240,10 +231,12 @@ async function resolveCluster(fetch, runtime, input) {
|
|
|
240
231
|
async function fetchClusterDetail(fetch, runtime, cluster, { allowMissing = false, signal } = {}) {
|
|
241
232
|
let payload;
|
|
242
233
|
try {
|
|
243
|
-
payload = await
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
234
|
+
payload = await gatewayApi.getReservationCluster(
|
|
235
|
+
fetch,
|
|
236
|
+
cluster.access_mode,
|
|
237
|
+
cluster.reservation_id,
|
|
238
|
+
signal
|
|
239
|
+
);
|
|
247
240
|
} catch (error) {
|
|
248
241
|
if (allowMissing && error?.status === 404) return null;
|
|
249
242
|
throw error;
|
|
@@ -263,7 +256,9 @@ async function fetchClusterDetail(fetch, runtime, cluster, { allowMissing = fals
|
|
|
263
256
|
|
|
264
257
|
async function fetchEligibility(fetch, runtime, reservationId, accessMode = "kubernetes") {
|
|
265
258
|
const payload = record(
|
|
266
|
-
await fetch
|
|
259
|
+
await gatewayApi.getReservationClusterEligibility(fetch, reservationId, {
|
|
260
|
+
access_mode: accessMode,
|
|
261
|
+
})
|
|
267
262
|
);
|
|
268
263
|
if (
|
|
269
264
|
text(payload.reservation_id) !== reservationId ||
|
|
@@ -558,10 +553,12 @@ function partialClusterExecution(runtime, cluster, accepted = {}) {
|
|
|
558
553
|
async function clusterAccessIsReady(fetch, cluster, signal) {
|
|
559
554
|
try {
|
|
560
555
|
const credentials = record(
|
|
561
|
-
await
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
556
|
+
await gatewayApi.getReservationClusterCredentials(
|
|
557
|
+
fetch,
|
|
558
|
+
cluster.access_mode,
|
|
559
|
+
cluster.reservation_id,
|
|
560
|
+
signal
|
|
561
|
+
)
|
|
565
562
|
);
|
|
566
563
|
if (cluster.access_mode === "slurm") {
|
|
567
564
|
return Boolean(text(credentials.login_host));
|
|
@@ -1280,19 +1277,18 @@ export async function launchClusterMode(
|
|
|
1280
1277
|
});
|
|
1281
1278
|
}
|
|
1282
1279
|
if (selectedKeyId && requested.ssh_key) requested.ssh_key.id = selectedKeyId;
|
|
1283
|
-
const response = await
|
|
1284
|
-
|
|
1280
|
+
const response = await gatewayApi.launchReservationCluster(
|
|
1281
|
+
context.fetch,
|
|
1282
|
+
accessMode,
|
|
1283
|
+
reservationId,
|
|
1285
1284
|
{
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
...(selectedKeyId ? { ssh_key_ids: [selectedKeyId] } : {}),
|
|
1294
|
-
},
|
|
1295
|
-
}
|
|
1285
|
+
network_mode: networkMode,
|
|
1286
|
+
...(membershipCount > 0 ? { node_count: membershipCount } : {}),
|
|
1287
|
+
node_ids: selectedIds,
|
|
1288
|
+
...(virtualMachineIds.length ? { virtual_machine_ids: virtualMachineIds } : {}),
|
|
1289
|
+
...(selectedKeyId ? { ssh_key_ids: [selectedKeyId] } : {}),
|
|
1290
|
+
},
|
|
1291
|
+
{ headers: { "Idempotency-Key": idempotencyKey } }
|
|
1296
1292
|
);
|
|
1297
1293
|
let cluster = projectCluster(response);
|
|
1298
1294
|
if (!UUID.test(cluster.id ?? "")) {
|
|
@@ -1555,13 +1551,11 @@ async function addNode(input, deps) {
|
|
|
1555
1551
|
code: "cluster_eligibility_changed",
|
|
1556
1552
|
});
|
|
1557
1553
|
}
|
|
1558
|
-
await
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
body: { node_id: machine.id },
|
|
1564
|
-
}
|
|
1554
|
+
await gatewayApi.addReservationClusterNode(
|
|
1555
|
+
deps.fetch,
|
|
1556
|
+
current.reservation_id,
|
|
1557
|
+
{ node_id: machine.id },
|
|
1558
|
+
{ headers: { "Idempotency-Key": prepared.idempotencyKey } }
|
|
1565
1559
|
);
|
|
1566
1560
|
const accepted = { membership_goal: goalIds, replayed: false };
|
|
1567
1561
|
if (!shouldWait(deps.interaction, input))
|
|
@@ -1647,13 +1641,9 @@ async function removeNode(input, deps) {
|
|
|
1647
1641
|
code: "cluster_membership_changed",
|
|
1648
1642
|
});
|
|
1649
1643
|
}
|
|
1650
|
-
await deps.fetch
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
method: "DELETE",
|
|
1654
|
-
headers: { "Idempotency-Key": prepared.idempotencyKey },
|
|
1655
|
-
}
|
|
1656
|
-
);
|
|
1644
|
+
await gatewayApi.removeReservationClusterNode(deps.fetch, current.reservation_id, machine.id, {
|
|
1645
|
+
headers: { "Idempotency-Key": prepared.idempotencyKey },
|
|
1646
|
+
});
|
|
1657
1647
|
const accepted = { membership_goal: goalIds, replayed: false };
|
|
1658
1648
|
if (!shouldWait(deps.interaction, input))
|
|
1659
1649
|
return deps.interaction.result({
|
|
@@ -1723,10 +1713,12 @@ async function teardownCluster(input, deps) {
|
|
|
1723
1713
|
code: "cluster_membership_changed",
|
|
1724
1714
|
});
|
|
1725
1715
|
}
|
|
1726
|
-
await
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1716
|
+
await gatewayApi.teardownReservationCluster(
|
|
1717
|
+
deps.fetch,
|
|
1718
|
+
current.access_mode,
|
|
1719
|
+
current.reservation_id,
|
|
1720
|
+
{ headers: { "Idempotency-Key": idempotencyKey } }
|
|
1721
|
+
);
|
|
1730
1722
|
const accepted = {
|
|
1731
1723
|
replayed: false,
|
|
1732
1724
|
capacity_reservation: { id: current.reservation_id, retained: true, charges_continue: true },
|
|
@@ -1909,7 +1901,11 @@ export async function readClusterModeConnectContext(state, accessMode) {
|
|
|
1909
1901
|
let credentialError = null;
|
|
1910
1902
|
try {
|
|
1911
1903
|
credentials = record(
|
|
1912
|
-
await
|
|
1904
|
+
await gatewayApi.getReservationClusterCredentials(
|
|
1905
|
+
state.context.fetch,
|
|
1906
|
+
current.access_mode,
|
|
1907
|
+
current.reservation_id
|
|
1908
|
+
)
|
|
1913
1909
|
);
|
|
1914
1910
|
} catch (error) {
|
|
1915
1911
|
const status = Number(error?.status);
|
|
@@ -2046,7 +2042,11 @@ export const kubernetesConnectModeHandler = {
|
|
|
2046
2042
|
});
|
|
2047
2043
|
}
|
|
2048
2044
|
const credentials = record(
|
|
2049
|
-
await
|
|
2045
|
+
await gatewayApi.getReservationClusterCredentials(
|
|
2046
|
+
state.context.fetch,
|
|
2047
|
+
current.access_mode,
|
|
2048
|
+
current.reservation_id
|
|
2049
|
+
)
|
|
2050
2050
|
);
|
|
2051
2051
|
const kubeconfig =
|
|
2052
2052
|
typeof credentials.kubeconfig === "string" && credentials.kubeconfig.trim()
|
package/src/mount-commands.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as gatewayApi from "../vendor/api-client/gateway.js";
|
|
2
|
+
import { CliApiError } from "./api-client.mjs";
|
|
2
3
|
import { isRecoverableWaitFailure } from "./interaction-runtime.mjs";
|
|
3
4
|
import { resolveVolume, safeVolume } from "./storage-commands.mjs";
|
|
4
5
|
|
|
@@ -118,18 +119,6 @@ function safeFailure(
|
|
|
118
119
|
});
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
function reservationAttachmentPath(reservationId) {
|
|
122
|
-
return computeEndpoint(
|
|
123
|
-
`/nodes/reservations/${encodeURIComponent(reservationId)}/storage-attachment`
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function reservationDeploymentPath(reservationId) {
|
|
128
|
-
return computeEndpoint(
|
|
129
|
-
`/nodes/reservations/${encodeURIComponent(reservationId)}/storage-deployment`
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
122
|
function stableAttachmentPhase(value) {
|
|
134
123
|
const state = text(value)?.toLowerCase() ?? "pending";
|
|
135
124
|
if (["attached", "ready"].includes(state)) return state;
|
|
@@ -355,10 +344,11 @@ async function withMachineCount(runtime, mount, reservationId, signal) {
|
|
|
355
344
|
|
|
356
345
|
async function fetchMount(runtime, reservationId, { preserveFailure = false, signal } = {}) {
|
|
357
346
|
try {
|
|
358
|
-
const payload = await
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
347
|
+
const payload = await gatewayApi.getReservationStorageAttachment(
|
|
348
|
+
runtime.fetch,
|
|
349
|
+
reservationId,
|
|
350
|
+
signal
|
|
351
|
+
);
|
|
362
352
|
return withMachineCount(
|
|
363
353
|
runtime,
|
|
364
354
|
projectVolumeMount(payload, reservationId),
|
|
@@ -679,15 +669,12 @@ export async function attachVolumeMount(input, runtime) {
|
|
|
679
669
|
}
|
|
680
670
|
|
|
681
671
|
try {
|
|
682
|
-
const payload = await
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
access_mode: access,
|
|
689
|
-
},
|
|
690
|
-
});
|
|
672
|
+
const payload = await gatewayApi.deployReservationStorage(
|
|
673
|
+
runtime.fetch,
|
|
674
|
+
reservationId,
|
|
675
|
+
{ drive_id: projectedVolume.id, mount_mode: "fuse", access_mode: access },
|
|
676
|
+
{ headers: idempotencyHeaders(input) }
|
|
677
|
+
);
|
|
691
678
|
current = await withMachineCount(
|
|
692
679
|
runtime,
|
|
693
680
|
projectVolumeMount(payload, reservationId),
|
|
@@ -787,10 +774,12 @@ export async function detachVolumeMount(input, runtime) {
|
|
|
787
774
|
}
|
|
788
775
|
current = refreshed;
|
|
789
776
|
try {
|
|
790
|
-
await
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
777
|
+
await gatewayApi.detachReservationStorage(
|
|
778
|
+
runtime.fetch,
|
|
779
|
+
reservationId,
|
|
780
|
+
{},
|
|
781
|
+
{ headers: idempotencyHeaders(input) }
|
|
782
|
+
);
|
|
794
783
|
} catch (error) {
|
|
795
784
|
if (error instanceof CliApiError && (error.status === undefined || error.status >= 500)) {
|
|
796
785
|
const reconciled = await fetchMount(runtime, reservationId);
|
package/src/vm-commands.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as gatewayApi from "../vendor/api-client/gateway.js";
|
|
1
2
|
import { getReservation } from "../vendor/api-client/commerce.js";
|
|
2
3
|
import { spawn as spawnProcessDefault } from "node:child_process";
|
|
3
4
|
import { createHash } from "node:crypto";
|
|
@@ -513,11 +514,11 @@ async function fetchOrganizationKeys(context) {
|
|
|
513
514
|
const seen = new Set();
|
|
514
515
|
let cursor = null;
|
|
515
516
|
do {
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
);
|
|
517
|
+
const page = await gatewayApi.listAccountSshKeys(context.fetch, {
|
|
518
|
+
status: "active",
|
|
519
|
+
limit: 500,
|
|
520
|
+
...(cursor ? { cursor } : {}),
|
|
521
|
+
});
|
|
521
522
|
keys.push(...keysFromPayload(page));
|
|
522
523
|
cursor = page.next_cursor ?? null;
|
|
523
524
|
if (cursor && seen.has(cursor)) throw new Error("SSH key pagination did not advance.");
|
|
@@ -648,11 +649,11 @@ export async function materializeKey(context, plan, interaction, idempotencyKey)
|
|
|
648
649
|
const replay = keys.find((key) => text(key.public_key) === publicKey);
|
|
649
650
|
if (replay) return replay;
|
|
650
651
|
const created = record(
|
|
651
|
-
await
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
652
|
+
await gatewayApi.createAccountSshKey(
|
|
653
|
+
context.fetch,
|
|
654
|
+
{ label: text(plan.key.label), public_key: publicKey },
|
|
655
|
+
{ headers: { "Idempotency-Key": idempotencyKey } }
|
|
656
|
+
)
|
|
656
657
|
);
|
|
657
658
|
if (!resourceId(created) || text(created.public_key) !== publicKey) {
|
|
658
659
|
interaction.error("The saved SSH key did not match the reviewed public key.", {
|
|
@@ -1385,15 +1386,14 @@ function projectVolumeMount(value, reservationId) {
|
|
|
1385
1386
|
}
|
|
1386
1387
|
|
|
1387
1388
|
async function bareMetalRelationships(context, reservationId) {
|
|
1388
|
-
const encoded = encodeURIComponent(reservationId);
|
|
1389
1389
|
const missingAttachment = (error) => {
|
|
1390
1390
|
if (!isReferenceMiss(error)) throw error;
|
|
1391
1391
|
return null;
|
|
1392
1392
|
};
|
|
1393
1393
|
const [network, storage] = await Promise.all([
|
|
1394
1394
|
context.fetch(`/v1/workflows/compute/networks/shared?overlay=true`),
|
|
1395
|
-
|
|
1396
|
-
.fetch
|
|
1395
|
+
gatewayApi
|
|
1396
|
+
.getReservationStorageAttachment(context.fetch, reservationId)
|
|
1397
1397
|
.catch(missingAttachment),
|
|
1398
1398
|
]);
|
|
1399
1399
|
return {
|
|
@@ -3945,11 +3945,11 @@ export async function executeSshKeyCommand(command, input, context, interaction)
|
|
|
3945
3945
|
if (command === "list") {
|
|
3946
3946
|
positional(input, 0, "ornn ssh-keys list [--limit <1-500>] [--cursor <cursor>]", interaction);
|
|
3947
3947
|
const limit = input.limit ?? 50;
|
|
3948
|
-
const
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
);
|
|
3948
|
+
const payload = await gatewayApi.listAccountSshKeys(context.fetch, {
|
|
3949
|
+
status: "active",
|
|
3950
|
+
limit,
|
|
3951
|
+
...(text(input.cursor) ? { cursor: input.cursor } : {}),
|
|
3952
|
+
});
|
|
3953
3953
|
const items = activeKeys(keysFromPayload(payload));
|
|
3954
3954
|
const page = {
|
|
3955
3955
|
items,
|
|
@@ -4039,13 +4039,9 @@ export async function executeSshKeyCommand(command, input, context, interaction)
|
|
|
4039
4039
|
presentation: { lines: ["SSH-key deletion declined."] },
|
|
4040
4040
|
});
|
|
4041
4041
|
}
|
|
4042
|
-
await context.fetch(
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
method: "DELETE",
|
|
4046
|
-
headers: { "Idempotency-Key": text(input.idempotencyKey) ?? interaction.idempotencyKey() },
|
|
4047
|
-
}
|
|
4048
|
-
);
|
|
4042
|
+
await gatewayApi.deleteAccountSshKey(context.fetch, resourceId(key), {
|
|
4043
|
+
headers: { "Idempotency-Key": text(input.idempotencyKey) ?? interaction.idempotencyKey() },
|
|
4044
|
+
});
|
|
4049
4045
|
return interaction.result({
|
|
4050
4046
|
data: { ssh_key: projectKey(key) },
|
|
4051
4047
|
outcome: { class: "ended", phase: "deleted" },
|