@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.
- package/README.md +32 -5
- package/package.json +4 -2
- package/src/admin-capacity-reservations.mjs +4 -1
- package/src/admin-command-handlers.mjs +2 -0
- package/src/admin-node-diagnostics.mjs +1 -5
- package/src/admin-node-lifecycle.mjs +23 -125
- package/src/admin-node-sanitize.mjs +243 -0
- package/src/catalog-shell.mjs +6 -6
- package/src/evidence-commands.mjs +4 -6
- package/src/external-bucket-commands.mjs +71 -23
- package/src/interaction-runtime.mjs +3 -0
- package/src/kubernetes-commands.mjs +16 -83
- package/src/machine-reference.mjs +33 -56
- package/src/slurm-commands.mjs +13 -2
- package/src/vm-commands.mjs +39 -59
- package/vendor/api-client/commerce.js +743 -0
- package/vendor/capabilities/cli.js +7688 -7
- package/vendor/capabilities/capabilities/admin-capacity-reservations.js +0 -101
- package/vendor/capabilities/capabilities/admin-enrollment.js +0 -322
- package/vendor/capabilities/capabilities/admin-node-diagnostics.js +0 -88
- package/vendor/capabilities/capabilities/admin-nodes-lifecycle.js +0 -137
- package/vendor/capabilities/capabilities/admin.js +0 -91
- package/vendor/capabilities/capabilities/cli.js +0 -156
- package/vendor/capabilities/capabilities/clusters.js +0 -248
- package/vendor/capabilities/capabilities/commerce-market.js +0 -405
- package/vendor/capabilities/capabilities/compute.js +0 -523
- package/vendor/capabilities/capabilities/credits.js +0 -83
- package/vendor/capabilities/capabilities/evidence.js +0 -145
- package/vendor/capabilities/capabilities/external-buckets.js +0 -191
- package/vendor/capabilities/capabilities/fleet.js +0 -386
- package/vendor/capabilities/capabilities/identity.js +0 -129
- package/vendor/capabilities/capabilities/listings.js +0 -398
- package/vendor/capabilities/capabilities/locality.js +0 -42
- package/vendor/capabilities/capabilities/market-shared.js +0 -46
- package/vendor/capabilities/capabilities/markets.js +0 -482
- package/vendor/capabilities/capabilities/mounts.js +0 -176
- package/vendor/capabilities/capabilities/native-listing.js +0 -99
- package/vendor/capabilities/capabilities/networks.js +0 -1264
- package/vendor/capabilities/capabilities/observability.js +0 -253
- package/vendor/capabilities/capabilities/organization-invites.js +0 -53
- package/vendor/capabilities/capabilities/reservation-list.js +0 -68
- package/vendor/capabilities/capabilities/staff-shared.js +0 -64
- package/vendor/capabilities/capabilities/tokens.js +0 -161
- package/vendor/capabilities/capabilities/users.js +0 -157
- package/vendor/capabilities/capabilities/volumes.js +0 -178
- package/vendor/capabilities/capability.js +0 -99
- package/vendor/capabilities/cli-catalog.js +0 -73
- package/vendor/capabilities/cli-migrations.js +0 -264
- package/vendor/capabilities/names.js +0 -6
- package/vendor/capabilities/software-catalog.js +0 -82
- package/vendor/capabilities/spec-catalog.js +0 -151
- package/vendor/capabilities/supplier-directory.js +0 -20
|
@@ -1,1264 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { defineCapability } from "../capability.js";
|
|
3
|
-
const CANONICAL_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
4
|
-
const DEFAULT_LIMIT = 50;
|
|
5
|
-
const unknownRecordSchema = z.record(z.string(), z.unknown());
|
|
6
|
-
const MAX_LIMIT = 500;
|
|
7
|
-
const NETWORKS_PATH = "/v1/workflows/compute/networks";
|
|
8
|
-
const globalInput = {
|
|
9
|
-
$positionals: z.array(z.string()).optional(),
|
|
10
|
-
guided: z.boolean().optional(),
|
|
11
|
-
idempotencyKey: z.string().optional(),
|
|
12
|
-
noBrowser: z.boolean().optional(),
|
|
13
|
-
noWait: z.boolean().optional(),
|
|
14
|
-
overwrite: z.boolean().optional(),
|
|
15
|
-
print: z.boolean().optional(),
|
|
16
|
-
wait: z.boolean().optional(),
|
|
17
|
-
yes: z.boolean().optional(),
|
|
18
|
-
};
|
|
19
|
-
const noOptionsInput = z.object(globalInput).strict();
|
|
20
|
-
const connectInput = z
|
|
21
|
-
.object({
|
|
22
|
-
...globalInput,
|
|
23
|
-
identityFile: z.string().optional(),
|
|
24
|
-
output: z.string().optional(),
|
|
25
|
-
user: z.string().optional(),
|
|
26
|
-
})
|
|
27
|
-
.strict();
|
|
28
|
-
const listInput = z
|
|
29
|
-
.object({ ...globalInput, cursor: z.string().optional(), limit: z.string().optional() })
|
|
30
|
-
.strict();
|
|
31
|
-
const networkCreateInput = z
|
|
32
|
-
.object({ ...globalInput, cidr: z.string().optional(), name: z.string().optional() })
|
|
33
|
-
.strict();
|
|
34
|
-
const networkUpdateInput = z.object({ ...globalInput, name: z.string().optional() }).strict();
|
|
35
|
-
const vpnConfigureInput = z
|
|
36
|
-
.object({
|
|
37
|
-
...globalInput,
|
|
38
|
-
name: z.string().optional(),
|
|
39
|
-
output: z.string().optional(),
|
|
40
|
-
privateKey: z.string().optional(),
|
|
41
|
-
})
|
|
42
|
-
.strict();
|
|
43
|
-
function responseStatus(error) {
|
|
44
|
-
if (!error || typeof error !== "object" || !("status" in error))
|
|
45
|
-
return null;
|
|
46
|
-
return typeof error.status === "number" ? error.status : null;
|
|
47
|
-
}
|
|
48
|
-
function interaction(context) {
|
|
49
|
-
// The app-owned runtime extends the shared catalog context only for CLI execution.
|
|
50
|
-
const cli = context.cli;
|
|
51
|
-
const runtime = cli?.interaction;
|
|
52
|
-
if (!runtime)
|
|
53
|
-
throw new Error("This capability must execute through the Ornn CLI interaction runtime.");
|
|
54
|
-
return runtime;
|
|
55
|
-
}
|
|
56
|
-
function text(value) {
|
|
57
|
-
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
58
|
-
}
|
|
59
|
-
function shellQuote(value) {
|
|
60
|
-
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
61
|
-
}
|
|
62
|
-
function record(value) {
|
|
63
|
-
const parsed = unknownRecordSchema.safeParse(value);
|
|
64
|
-
return parsed.success ? parsed.data : {};
|
|
65
|
-
}
|
|
66
|
-
function integer(value) {
|
|
67
|
-
return typeof value === "number" && Number.isInteger(value) ? value : null;
|
|
68
|
-
}
|
|
69
|
-
function normalizedName(value) {
|
|
70
|
-
return value.normalize("NFKC").trim().replace(/\s+/g, " ").toLocaleLowerCase("en-US");
|
|
71
|
-
}
|
|
72
|
-
function normalizedCidr(value) {
|
|
73
|
-
const [address, rawPrefix, ...extra] = value.trim().split("/");
|
|
74
|
-
const octets = address?.split(".").map(Number) ?? [];
|
|
75
|
-
const prefix = Number(rawPrefix);
|
|
76
|
-
if (extra.length ||
|
|
77
|
-
octets.length !== 4 ||
|
|
78
|
-
octets.some((octet) => !Number.isInteger(octet) || octet < 0 || octet > 255) ||
|
|
79
|
-
!Number.isInteger(prefix) ||
|
|
80
|
-
prefix < 0 ||
|
|
81
|
-
prefix > 32) {
|
|
82
|
-
return value.trim();
|
|
83
|
-
}
|
|
84
|
-
const numeric = (((octets[0] * 256 + octets[1]) * 256 + octets[2]) * 256 + octets[3]) >>> 0;
|
|
85
|
-
const mask = prefix === 0 ? 0 : (0xffffffff << (32 - prefix)) >>> 0;
|
|
86
|
-
const network = (numeric & mask) >>> 0;
|
|
87
|
-
return `${network >>> 24}.${(network >>> 16) & 0xff}.${(network >>> 8) & 0xff}.${network & 0xff}/${prefix}`;
|
|
88
|
-
}
|
|
89
|
-
function requiredOption(runtime, value, option, usage) {
|
|
90
|
-
const parsed = text(value);
|
|
91
|
-
if (!parsed)
|
|
92
|
-
runtime.usage(`${option} is required.\n${usage}`, { field: option.slice(2) });
|
|
93
|
-
return parsed;
|
|
94
|
-
}
|
|
95
|
-
function exactPositionals(runtime, positionals, count, usage) {
|
|
96
|
-
const values = positionals ?? [];
|
|
97
|
-
if (values.length !== count || values.some((value) => !value.trim()))
|
|
98
|
-
runtime.usage(usage);
|
|
99
|
-
return values.map((value) => value.trim());
|
|
100
|
-
}
|
|
101
|
-
function requireCanonicalTarget(runtime, value) {
|
|
102
|
-
if (!CANONICAL_ID.test(value)) {
|
|
103
|
-
runtime.usage("Private targets currently require a canonical capacity-reservation identifier so connect cannot select an ambiguous resource.", { code: "canonical_reference_required", field: "target" });
|
|
104
|
-
}
|
|
105
|
-
return value;
|
|
106
|
-
}
|
|
107
|
-
function requireMutationKey(runtime, input) {
|
|
108
|
-
if (!runtime.machine)
|
|
109
|
-
return text(input.idempotencyKey);
|
|
110
|
-
const key = text(input.idempotencyKey);
|
|
111
|
-
if (!key) {
|
|
112
|
-
runtime.usage("Machine mutations require --idempotency-key <caller-stable-key> before API I/O.", {
|
|
113
|
-
code: "idempotency_key_required",
|
|
114
|
-
field: "idempotencyKey",
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
return key;
|
|
118
|
-
}
|
|
119
|
-
function mutationHeaders(key) {
|
|
120
|
-
return key ? { "Idempotency-Key": key } : undefined;
|
|
121
|
-
}
|
|
122
|
-
function ownerErrorCode(error) {
|
|
123
|
-
if (!error || typeof error !== "object" || !("detail" in error))
|
|
124
|
-
return null;
|
|
125
|
-
const detail = record(error.detail);
|
|
126
|
-
const nested = detail.detail ?? detail.error;
|
|
127
|
-
if (typeof nested === "string")
|
|
128
|
-
return nested;
|
|
129
|
-
return text(record(nested).code) ?? text(detail.code);
|
|
130
|
-
}
|
|
131
|
-
function lifecycle(statusValue) {
|
|
132
|
-
const status = text(statusValue)?.toLowerCase() ?? "unknown";
|
|
133
|
-
if (["active", "ready", "applied", "connected"].includes(status)) {
|
|
134
|
-
return { class: "ready", phase: "available" };
|
|
135
|
-
}
|
|
136
|
-
if (["failed", "degraded", "error"].includes(status)) {
|
|
137
|
-
return { class: "failed", phase: "needs_attention" };
|
|
138
|
-
}
|
|
139
|
-
if (["deleted", "revoked", "disconnected"].includes(status)) {
|
|
140
|
-
return { class: "ended", phase: "disconnected" };
|
|
141
|
-
}
|
|
142
|
-
if (["deleting", "revoking"].includes(status)) {
|
|
143
|
-
return { class: "working", phase: "disconnecting" };
|
|
144
|
-
}
|
|
145
|
-
return { class: "pending", phase: "configuring" };
|
|
146
|
-
}
|
|
147
|
-
function reservationTargetLifecycle(statusValue) {
|
|
148
|
-
const status = text(statusValue)?.toLowerCase() ?? "unknown";
|
|
149
|
-
if (status === "active")
|
|
150
|
-
return { class: "ready", phase: "active" };
|
|
151
|
-
if (["failed", "payment_failed", "error"].includes(status)) {
|
|
152
|
-
return { class: "failed", phase: status };
|
|
153
|
-
}
|
|
154
|
-
if (["cancelled", "canceled", "completed", "ended", "expired", "rejected", "terminated"].includes(status)) {
|
|
155
|
-
return { class: "ended", phase: status };
|
|
156
|
-
}
|
|
157
|
-
return { class: "pending", phase: status };
|
|
158
|
-
}
|
|
159
|
-
function projectNetwork(value) {
|
|
160
|
-
const source = record(value);
|
|
161
|
-
const attachmentIds = Array.isArray(source.attached_reservation_ids)
|
|
162
|
-
? source.attached_reservation_ids
|
|
163
|
-
.map((value) => text(value))
|
|
164
|
-
.filter((value) => Boolean(value && CANONICAL_ID.test(value)))
|
|
165
|
-
: [];
|
|
166
|
-
return {
|
|
167
|
-
id: text(source.id),
|
|
168
|
-
name: text(source.name) ?? "Unnamed private network",
|
|
169
|
-
cidr: text(source.cidr),
|
|
170
|
-
lifecycle: lifecycle(source.status),
|
|
171
|
-
attachment_count: integer(source.attached_reservation_count) ?? attachmentIds.length,
|
|
172
|
-
attached_reservation_ids: [...new Set(attachmentIds)],
|
|
173
|
-
created_at: text(source.created_at),
|
|
174
|
-
updated_at: text(source.updated_at),
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
function networkRows(payload) {
|
|
178
|
-
const source = record(payload);
|
|
179
|
-
const values = Array.isArray(payload)
|
|
180
|
-
? payload
|
|
181
|
-
: Array.isArray(source.networks)
|
|
182
|
-
? source.networks
|
|
183
|
-
: [];
|
|
184
|
-
return values
|
|
185
|
-
.flatMap((value) => {
|
|
186
|
-
const parsed = unknownRecordSchema.safeParse(value);
|
|
187
|
-
return parsed.success ? [parsed.data] : [];
|
|
188
|
-
})
|
|
189
|
-
.sort((left, right) => {
|
|
190
|
-
const byName = normalizedName(text(left.name) ?? "").localeCompare(normalizedName(text(right.name) ?? ""));
|
|
191
|
-
return byName || (text(left.id) ?? "").localeCompare(text(right.id) ?? "");
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
function networkFromMutation(payload) {
|
|
195
|
-
const source = record(payload);
|
|
196
|
-
return projectNetwork(source.network ?? source);
|
|
197
|
-
}
|
|
198
|
-
async function fetchNetworks(context) {
|
|
199
|
-
return networkRows(await context.fetch(NETWORKS_PATH));
|
|
200
|
-
}
|
|
201
|
-
async function resolveNetwork(context, runtime, reference) {
|
|
202
|
-
if (CANONICAL_ID.test(reference)) {
|
|
203
|
-
return record(await context.fetch(`${NETWORKS_PATH}/${encodeURIComponent(reference)}`));
|
|
204
|
-
}
|
|
205
|
-
const wanted = normalizedName(reference);
|
|
206
|
-
const matches = (await fetchNetworks(context)).filter((candidate) => normalizedName(text(candidate.name) ?? "") === wanted);
|
|
207
|
-
if (matches.length === 0) {
|
|
208
|
-
runtime.error(`Private network not found: ${reference}.`, {
|
|
209
|
-
code: "private_network_not_found",
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
if (matches.length > 1) {
|
|
213
|
-
runtime.usage(`More than one private network has the exact name "${reference}"; use a canonical identifier.`, {
|
|
214
|
-
code: "private_network_ambiguous",
|
|
215
|
-
field: "network",
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
return matches[0];
|
|
219
|
-
}
|
|
220
|
-
function encodeCursor(command, offset) {
|
|
221
|
-
return encodeURIComponent(JSON.stringify({ command, offset }));
|
|
222
|
-
}
|
|
223
|
-
function decodeCursor(runtime, command, cursor) {
|
|
224
|
-
if (!cursor)
|
|
225
|
-
return 0;
|
|
226
|
-
try {
|
|
227
|
-
const parsed = record(JSON.parse(decodeURIComponent(cursor)));
|
|
228
|
-
if (parsed.command === command &&
|
|
229
|
-
Number.isInteger(parsed.offset) &&
|
|
230
|
-
Number(parsed.offset) >= 0) {
|
|
231
|
-
return Number(parsed.offset);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
catch {
|
|
235
|
-
// The stable usage error below intentionally hides cursor internals.
|
|
236
|
-
}
|
|
237
|
-
runtime.usage("The pagination cursor is invalid or belongs to another command.", {
|
|
238
|
-
code: "invalid_cursor",
|
|
239
|
-
field: "cursor",
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
function pageLimit(runtime, raw) {
|
|
243
|
-
if (raw === undefined)
|
|
244
|
-
return DEFAULT_LIMIT;
|
|
245
|
-
const limit = Number(raw);
|
|
246
|
-
if (!Number.isInteger(limit) || limit < 1 || limit > MAX_LIMIT) {
|
|
247
|
-
runtime.usage(`--limit must be an integer from 1 to ${MAX_LIMIT}.`, { field: "limit" });
|
|
248
|
-
}
|
|
249
|
-
return limit;
|
|
250
|
-
}
|
|
251
|
-
function page(runtime, command, rows, input) {
|
|
252
|
-
const limit = pageLimit(runtime, input.limit);
|
|
253
|
-
const offset = decodeCursor(runtime, command, input.cursor);
|
|
254
|
-
const items = rows.slice(offset, offset + limit);
|
|
255
|
-
const nextOffset = offset + items.length;
|
|
256
|
-
return {
|
|
257
|
-
items,
|
|
258
|
-
page: {
|
|
259
|
-
limit,
|
|
260
|
-
returned: items.length,
|
|
261
|
-
next_cursor: nextOffset < rows.length ? encodeCursor(command, nextOffset) : null,
|
|
262
|
-
},
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
function declined(runtime) {
|
|
266
|
-
return runtime.result({
|
|
267
|
-
data: null,
|
|
268
|
-
errors: [{ code: "action_declined", message: "No changes were made.", retryable: false }],
|
|
269
|
-
exitCode: 1,
|
|
270
|
-
outcome: { class: "ended", phase: "declined" },
|
|
271
|
-
presentation: { lines: ["No changes were made."] },
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
function networkLines(network) {
|
|
275
|
-
const state = record(network.lifecycle);
|
|
276
|
-
return [
|
|
277
|
-
`${String(network.name)} (${String(network.id ?? "unknown")})`,
|
|
278
|
-
` CIDR: ${String(network.cidr ?? "pending")}`,
|
|
279
|
-
` State: ${state.class} (${state.phase})`,
|
|
280
|
-
];
|
|
281
|
-
}
|
|
282
|
-
function networkMutationExecution(runtime, network, replayed) {
|
|
283
|
-
const state = record(network.lifecycle);
|
|
284
|
-
const stateClass = String(state.class);
|
|
285
|
-
const ready = stateClass === "ready";
|
|
286
|
-
const failed = stateClass === "failed";
|
|
287
|
-
const command = ready ? "ornn connect" : `ornn private-networks show ${String(network.id)}`;
|
|
288
|
-
return runtime.result({
|
|
289
|
-
action: {
|
|
290
|
-
command,
|
|
291
|
-
description: ready
|
|
292
|
-
? "Continue to an exact compute target."
|
|
293
|
-
: "Inspect private-network readiness before attaching or connecting.",
|
|
294
|
-
type: "command",
|
|
295
|
-
},
|
|
296
|
-
data: { private_network: network, replayed },
|
|
297
|
-
exitCode: ready ? 0 : failed ? 4 : 3,
|
|
298
|
-
outcome: ready
|
|
299
|
-
? { class: "ready", phase: "active" }
|
|
300
|
-
: { class: String(state.class), phase: String(state.phase) },
|
|
301
|
-
presentation: {
|
|
302
|
-
lines: [
|
|
303
|
-
ready
|
|
304
|
-
? replayed
|
|
305
|
-
? "Private network already satisfies this request."
|
|
306
|
-
: "Private network created."
|
|
307
|
-
: failed
|
|
308
|
-
? "Private network exists, but provisioning failed."
|
|
309
|
-
: "Private network creation was accepted and is still provisioning.",
|
|
310
|
-
...networkLines(network),
|
|
311
|
-
],
|
|
312
|
-
next: command,
|
|
313
|
-
},
|
|
314
|
-
resume: ready ? null : { kind: "private_network", ref: String(network.id) },
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
async function listPrivateNetworks(context, input) {
|
|
318
|
-
const runtime = interaction(context);
|
|
319
|
-
exactPositionals(runtime, input.$positionals, 0, "Usage: ornn private-networks list [--limit <1-500>] [--cursor <cursor>]");
|
|
320
|
-
const rows = (await fetchNetworks(context)).map(projectNetwork);
|
|
321
|
-
const selected = page(runtime, "private-networks list", rows, input);
|
|
322
|
-
return runtime.result({
|
|
323
|
-
data: { private_networks: selected.items },
|
|
324
|
-
page: selected.page,
|
|
325
|
-
presentation: {
|
|
326
|
-
lines: selected.items.length
|
|
327
|
-
? selected.items.flatMap(networkLines)
|
|
328
|
-
: ["No private networks found."],
|
|
329
|
-
...(selected.page.next_cursor
|
|
330
|
-
? { next: `ornn private-networks list --cursor ${selected.page.next_cursor}` }
|
|
331
|
-
: {}),
|
|
332
|
-
},
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
async function showPrivateNetwork(context, input) {
|
|
336
|
-
const runtime = interaction(context);
|
|
337
|
-
const [reference] = exactPositionals(runtime, input.$positionals, 1, "Usage: ornn private-networks show <exact-name-or-id>");
|
|
338
|
-
const network = projectNetwork(await resolveNetwork(context, runtime, reference));
|
|
339
|
-
return runtime.result({
|
|
340
|
-
data: { private_network: network },
|
|
341
|
-
presentation: { lines: networkLines(network) },
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
async function createPrivateNetwork(context, input) {
|
|
345
|
-
const runtime = interaction(context);
|
|
346
|
-
const usage = "Usage: ornn private-networks create --name <name> [--cidr <cidr>]";
|
|
347
|
-
exactPositionals(runtime, input.$positionals, 0, usage);
|
|
348
|
-
if (!text(input.name) && input.guided === true && !runtime.machine) {
|
|
349
|
-
input.name = await runtime.ask({ label: "Private network name" });
|
|
350
|
-
}
|
|
351
|
-
const name = requiredOption(runtime, input.name, "--name", usage)
|
|
352
|
-
.normalize("NFKC")
|
|
353
|
-
.trim()
|
|
354
|
-
.replace(/\s+/g, " ");
|
|
355
|
-
const cidr = text(input.cidr) ? normalizedCidr(String(input.cidr)) : null;
|
|
356
|
-
const idempotencyKey = requireMutationKey(runtime, input);
|
|
357
|
-
const matches = (await fetchNetworks(context)).filter((candidate) => normalizedName(text(candidate.name) ?? "") === normalizedName(name));
|
|
358
|
-
if (matches.length > 1) {
|
|
359
|
-
runtime.usage(`More than one private network has the exact name "${name}"; use a different name.`, {
|
|
360
|
-
code: "private_network_ambiguous",
|
|
361
|
-
field: "name",
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
if (matches.length === 1) {
|
|
365
|
-
const existingCidr = text(matches[0].cidr);
|
|
366
|
-
if (cidr && (!existingCidr || normalizedCidr(existingCidr) !== cidr)) {
|
|
367
|
-
runtime.usage(`Private network "${name}" already exists with different effective input. Use a new idempotency key and name after review.`, { code: "private_network_replay_conflict", field: "cidr" });
|
|
368
|
-
}
|
|
369
|
-
return networkMutationExecution(runtime, projectNetwork(matches[0]), true);
|
|
370
|
-
}
|
|
371
|
-
if (!(await runtime.review({
|
|
372
|
-
title: "Create private network",
|
|
373
|
-
terms: [
|
|
374
|
-
{ label: "Name", value: name },
|
|
375
|
-
{ label: "CIDR", value: cidr ?? "Automatically assigned" },
|
|
376
|
-
],
|
|
377
|
-
})))
|
|
378
|
-
return declined(runtime);
|
|
379
|
-
const created = await context.fetch(NETWORKS_PATH, {
|
|
380
|
-
body: { name, ...(cidr ? { cidr } : {}) },
|
|
381
|
-
headers: mutationHeaders(idempotencyKey),
|
|
382
|
-
method: "POST",
|
|
383
|
-
});
|
|
384
|
-
return networkMutationExecution(runtime, networkFromMutation(created), false);
|
|
385
|
-
}
|
|
386
|
-
async function updatePrivateNetwork(context, input) {
|
|
387
|
-
const runtime = interaction(context);
|
|
388
|
-
const usage = "Usage: ornn private-networks update <exact-name-or-id> --name <new-name>";
|
|
389
|
-
const [reference] = exactPositionals(runtime, input.$positionals, 1, usage);
|
|
390
|
-
const name = requiredOption(runtime, input.name, "--name", usage)
|
|
391
|
-
.normalize("NFKC")
|
|
392
|
-
.trim()
|
|
393
|
-
.replace(/\s+/g, " ");
|
|
394
|
-
const idempotencyKey = requireMutationKey(runtime, input);
|
|
395
|
-
const current = await resolveNetwork(context, runtime, reference);
|
|
396
|
-
if (normalizedName(text(current.name) ?? "") === normalizedName(name)) {
|
|
397
|
-
const network = projectNetwork(current);
|
|
398
|
-
return runtime.result({
|
|
399
|
-
data: { private_network: network, replayed: true },
|
|
400
|
-
presentation: {
|
|
401
|
-
lines: ["Private network already has the requested name.", ...networkLines(network)],
|
|
402
|
-
},
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
if ((await fetchNetworks(context)).some((candidate) => normalizedName(text(candidate.name) ?? "") === normalizedName(name))) {
|
|
406
|
-
runtime.usage(`Another private network already has the exact name "${name}".`, {
|
|
407
|
-
code: "private_network_name_conflict",
|
|
408
|
-
field: "name",
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
if (!(await runtime.review({
|
|
412
|
-
title: "Update private network",
|
|
413
|
-
terms: [
|
|
414
|
-
{
|
|
415
|
-
label: "Private network",
|
|
416
|
-
value: `${text(current.name) ?? reference} (${text(current.id) ?? reference})`,
|
|
417
|
-
},
|
|
418
|
-
{ label: "New name", value: name },
|
|
419
|
-
],
|
|
420
|
-
})))
|
|
421
|
-
return declined(runtime);
|
|
422
|
-
const updated = await context.fetch(`${NETWORKS_PATH}/${encodeURIComponent(String(current.id))}`, {
|
|
423
|
-
body: { name },
|
|
424
|
-
headers: mutationHeaders(idempotencyKey),
|
|
425
|
-
method: "PATCH",
|
|
426
|
-
});
|
|
427
|
-
const network = networkFromMutation(updated);
|
|
428
|
-
return runtime.result({
|
|
429
|
-
data: { private_network: network, replayed: false },
|
|
430
|
-
presentation: { lines: ["Private network updated.", ...networkLines(network)] },
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
async function deletePrivateNetwork(context, input) {
|
|
434
|
-
const runtime = interaction(context);
|
|
435
|
-
const [reference] = exactPositionals(runtime, input.$positionals, 1, "Usage: ornn private-networks delete <exact-name-or-id>");
|
|
436
|
-
const idempotencyKey = requireMutationKey(runtime, input);
|
|
437
|
-
const projected = projectNetwork(await resolveNetwork(context, runtime, reference));
|
|
438
|
-
if (!(await runtime.review({
|
|
439
|
-
title: "Delete private network",
|
|
440
|
-
terms: [
|
|
441
|
-
{ label: "Private network", value: `${String(projected.name)} (${String(projected.id)})` },
|
|
442
|
-
{
|
|
443
|
-
label: "Consequence",
|
|
444
|
-
value: "Permanently delete this organization private network when the owner permits deletion.",
|
|
445
|
-
},
|
|
446
|
-
],
|
|
447
|
-
prompt: "Delete this private network?",
|
|
448
|
-
})))
|
|
449
|
-
return declined(runtime);
|
|
450
|
-
try {
|
|
451
|
-
await context.fetch(`${NETWORKS_PATH}/${encodeURIComponent(String(projected.id))}`, {
|
|
452
|
-
headers: mutationHeaders(idempotencyKey),
|
|
453
|
-
method: "DELETE",
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
catch (error) {
|
|
457
|
-
if (ownerErrorCode(error) === "network_in_use") {
|
|
458
|
-
throw error;
|
|
459
|
-
}
|
|
460
|
-
throw error;
|
|
461
|
-
}
|
|
462
|
-
return runtime.result({
|
|
463
|
-
data: { deleted: true, private_network: projected },
|
|
464
|
-
outcome: { class: "ended", phase: "deleted" },
|
|
465
|
-
presentation: {
|
|
466
|
-
lines: [`Private network deleted: ${String(projected.name)} (${String(projected.id)})`],
|
|
467
|
-
},
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
function vpnPath(reservationId) {
|
|
471
|
-
return `/v1/workflows/compute/nodes/reservations/${encodeURIComponent(reservationId)}/vpn/peers`;
|
|
472
|
-
}
|
|
473
|
-
function vpnPayloadRows(payload) {
|
|
474
|
-
const source = record(payload);
|
|
475
|
-
const peers = Array.isArray(source.peers) ? source.peers : [];
|
|
476
|
-
return peers.flatMap((value) => {
|
|
477
|
-
const parsed = unknownRecordSchema.safeParse(value);
|
|
478
|
-
return parsed.success ? [parsed.data] : [];
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
function vpnPeer(payload) {
|
|
482
|
-
const peer = record(payload.peer);
|
|
483
|
-
return Object.keys(peer).length ? peer : payload;
|
|
484
|
-
}
|
|
485
|
-
function projectVpnConnection(payload, reservationId) {
|
|
486
|
-
const peer = vpnPeer(payload);
|
|
487
|
-
const network = record(payload.network);
|
|
488
|
-
return {
|
|
489
|
-
id: text(peer.id),
|
|
490
|
-
name: text(peer.label) ?? "VPN connection",
|
|
491
|
-
reservation_id: reservationId,
|
|
492
|
-
private_network_id: text(network.network_id) ?? text(network.id),
|
|
493
|
-
assigned_address: text(peer.assigned_ip),
|
|
494
|
-
lifecycle: lifecycle(peer.status),
|
|
495
|
-
created_at: text(peer.created_at),
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
function vpnLines(connection) {
|
|
499
|
-
const state = record(connection.lifecycle);
|
|
500
|
-
return [
|
|
501
|
-
`${String(connection.name)} (${String(connection.id ?? "unknown")})`,
|
|
502
|
-
` Private target: ${String(connection.reservation_id)}`,
|
|
503
|
-
` State: ${state.class} (${state.phase})`,
|
|
504
|
-
];
|
|
505
|
-
}
|
|
506
|
-
async function listVpnPayloads(context, target) {
|
|
507
|
-
return vpnPayloadRows(await context.fetch(vpnPath(target)));
|
|
508
|
-
}
|
|
509
|
-
async function listVpnConnections(context, input) {
|
|
510
|
-
const runtime = interaction(context);
|
|
511
|
-
const [rawTarget] = exactPositionals(runtime, input.$positionals, 1, "Usage: ornn vpn-connections list <canonical-reservation-id> [--limit <1-500>] [--cursor <cursor>]");
|
|
512
|
-
const target = requireCanonicalTarget(runtime, rawTarget);
|
|
513
|
-
const connections = (await listVpnPayloads(context, target))
|
|
514
|
-
.map((payload) => projectVpnConnection(payload, target))
|
|
515
|
-
.sort((left, right) => normalizedName(String(left.name)).localeCompare(normalizedName(String(right.name))));
|
|
516
|
-
const selected = page(runtime, "vpn-connections list", connections, input);
|
|
517
|
-
return runtime.result({
|
|
518
|
-
data: { vpn_connections: selected.items },
|
|
519
|
-
page: selected.page,
|
|
520
|
-
presentation: {
|
|
521
|
-
lines: selected.items.length
|
|
522
|
-
? selected.items.flatMap(vpnLines)
|
|
523
|
-
: [`No VPN connections found for private target ${target}.`],
|
|
524
|
-
...(selected.page.next_cursor
|
|
525
|
-
? { next: `ornn vpn-connections list ${target} --cursor ${selected.page.next_cursor}` }
|
|
526
|
-
: {}),
|
|
527
|
-
},
|
|
528
|
-
});
|
|
529
|
-
}
|
|
530
|
-
function validateSecretExport(runtime, input) {
|
|
531
|
-
const hasOutput = Boolean(text(input.output));
|
|
532
|
-
const shouldPrint = input.print === true;
|
|
533
|
-
if (hasOutput === shouldPrint) {
|
|
534
|
-
runtime.usage("Choose exactly one explicit VPN configuration destination: --print or --output <path>.", {
|
|
535
|
-
code: "secret_destination_required",
|
|
536
|
-
field: "output",
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
if (shouldPrint && runtime.formatProvided) {
|
|
540
|
-
runtime.usage("--print is incompatible with an explicit --format.", {
|
|
541
|
-
code: "secret_print_format_conflict",
|
|
542
|
-
field: "format",
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
if (runtime.machine && shouldPrint) {
|
|
546
|
-
runtime.usage("Machine mode cannot mix secret bytes with a result envelope; use --output <path>.", {
|
|
547
|
-
code: "secret_print_unsupported_in_machine_mode",
|
|
548
|
-
field: "print",
|
|
549
|
-
});
|
|
550
|
-
}
|
|
551
|
-
if (!runtime.exportSecret ||
|
|
552
|
-
!runtime.prepareSecretExport ||
|
|
553
|
-
!runtime.wireGuardPublicKeyFromFile) {
|
|
554
|
-
runtime.error("The CLI secret-output service is unavailable.", {
|
|
555
|
-
code: "secret_output_unavailable",
|
|
556
|
-
exitCode: 6,
|
|
557
|
-
retryable: true,
|
|
558
|
-
});
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
async function prepareVpnExport(runtime, input) {
|
|
562
|
-
try {
|
|
563
|
-
await runtime.prepareSecretExport({
|
|
564
|
-
outputPath: text(input.output) ?? undefined,
|
|
565
|
-
overwrite: input.overwrite === true,
|
|
566
|
-
print: input.print === true,
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
catch (error) {
|
|
570
|
-
const code = error && typeof error === "object" && "code" in error ? text(error.code) : null;
|
|
571
|
-
const message = error instanceof Error && error.message
|
|
572
|
-
? error.message
|
|
573
|
-
: "Could not prepare the VPN configuration destination.";
|
|
574
|
-
runtime.error(message, { code: code ?? "vpn_configuration_export_failed" });
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
async function exportVpnConfig(runtime, input, content, privateKeyPath, publicKey) {
|
|
578
|
-
const config = typeof content === "string" && content.length > 0 ? content : null;
|
|
579
|
-
if (!config) {
|
|
580
|
-
runtime.error("Ornn did not return a usable VPN configuration.", {
|
|
581
|
-
code: "vpn_configuration_unavailable",
|
|
582
|
-
exitCode: 6,
|
|
583
|
-
retryable: true,
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
try {
|
|
587
|
-
return await runtime.exportSecret({
|
|
588
|
-
content: config,
|
|
589
|
-
outputPath: text(input.output) ?? undefined,
|
|
590
|
-
overwrite: input.overwrite === true,
|
|
591
|
-
print: input.print === true,
|
|
592
|
-
privateKeyPath,
|
|
593
|
-
publicKey,
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
|
-
catch (error) {
|
|
597
|
-
const code = error && typeof error === "object" && "code" in error ? text(error.code) : null;
|
|
598
|
-
const message = error instanceof Error && error.message
|
|
599
|
-
? error.message
|
|
600
|
-
: "Could not export the VPN configuration.";
|
|
601
|
-
runtime.error(message, { code: code ?? "vpn_configuration_export_failed" });
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
function vpnConfigureExecution(runtime, target, connection, exported, replayed) {
|
|
605
|
-
const state = record(connection.lifecycle);
|
|
606
|
-
const stateClass = String(state.class);
|
|
607
|
-
const ready = stateClass === "ready";
|
|
608
|
-
const failed = stateClass === "failed";
|
|
609
|
-
const activationCommand = exported.path
|
|
610
|
-
? `sudo wg-quick up ${shellQuote(exported.path)}`
|
|
611
|
-
: "sudo wg-quick up <saved-configuration-path>";
|
|
612
|
-
const action = ready
|
|
613
|
-
? {
|
|
614
|
-
command: activationCommand,
|
|
615
|
-
description: "Activate the completed local WireGuard configuration before connecting.",
|
|
616
|
-
type: "command",
|
|
617
|
-
}
|
|
618
|
-
: {
|
|
619
|
-
command: `ornn vpn-connections configure ${target} --guided`,
|
|
620
|
-
description: failed
|
|
621
|
-
? "Retry applying this exact VPN peer with its original private key."
|
|
622
|
-
: "Reapply this pending VPN peer after gateway readiness is restored.",
|
|
623
|
-
type: "command",
|
|
624
|
-
};
|
|
625
|
-
return runtime.result({
|
|
626
|
-
action,
|
|
627
|
-
data: { export: exported, replayed, vpn_connection: connection },
|
|
628
|
-
exitCode: ready ? 3 : failed ? 4 : 3,
|
|
629
|
-
outcome: ready
|
|
630
|
-
? { class: "action_required", phase: "local_vpn_activation_required" }
|
|
631
|
-
: { class: String(state.class), phase: String(state.phase) },
|
|
632
|
-
presentation: {
|
|
633
|
-
lines: [
|
|
634
|
-
ready
|
|
635
|
-
? replayed
|
|
636
|
-
? "VPN configuration exported again; local tunnel activation is still required."
|
|
637
|
-
: "VPN peer is active and the complete local configuration was exported."
|
|
638
|
-
: failed
|
|
639
|
-
? "VPN connection was saved, but the gateway rejected or could not apply it."
|
|
640
|
-
: "VPN connection was saved and is waiting for gateway application.",
|
|
641
|
-
...vpnLines(connection),
|
|
642
|
-
`Configuration fingerprint: ${exported.fingerprint}`,
|
|
643
|
-
...(exported.path ? [`Configuration written to ${exported.path} (${exported.mode}).`] : []),
|
|
644
|
-
],
|
|
645
|
-
next: action.command,
|
|
646
|
-
},
|
|
647
|
-
resume: ready
|
|
648
|
-
? { kind: "connect", ref: target }
|
|
649
|
-
: { kind: "vpn_connection", ref: String(connection.id) },
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
async function configureVpnConnection(context, input) {
|
|
653
|
-
const runtime = interaction(context);
|
|
654
|
-
const usage = "Usage: ornn vpn-connections configure <canonical-reservation-id> --name <name> --private-key <path> (--print | --output <path>)";
|
|
655
|
-
const [rawTarget] = exactPositionals(runtime, input.$positionals, 1, usage);
|
|
656
|
-
const target = requireCanonicalTarget(runtime, rawTarget);
|
|
657
|
-
if (input.guided === true && !runtime.machine) {
|
|
658
|
-
input.name ??= await runtime.ask({ label: "VPN connection name" });
|
|
659
|
-
input.privateKey ??= await runtime.ask({ label: "WireGuard private-key path" });
|
|
660
|
-
if (input.print !== true && !text(input.output)) {
|
|
661
|
-
input.output = await runtime.ask({ label: "VPN configuration output path" });
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
const name = requiredOption(runtime, input.name, "--name", usage)
|
|
665
|
-
.normalize("NFKC")
|
|
666
|
-
.trim()
|
|
667
|
-
.replace(/\s+/g, " ");
|
|
668
|
-
const privateKeyPath = requiredOption(runtime, input.privateKey, "--private-key", usage);
|
|
669
|
-
validateSecretExport(runtime, input);
|
|
670
|
-
await prepareVpnExport(runtime, input);
|
|
671
|
-
let publicKey = "";
|
|
672
|
-
try {
|
|
673
|
-
publicKey = await runtime.wireGuardPublicKeyFromFile(privateKeyPath);
|
|
674
|
-
}
|
|
675
|
-
catch (error) {
|
|
676
|
-
const code = error && typeof error === "object" && "code" in error ? text(error.code) : null;
|
|
677
|
-
const message = error instanceof Error && error.message
|
|
678
|
-
? error.message
|
|
679
|
-
: "Could not read the WireGuard private key.";
|
|
680
|
-
runtime.error(message, { code: code ?? "invalid_wireguard_private_key", field: "privateKey" });
|
|
681
|
-
}
|
|
682
|
-
const idempotencyKey = requireMutationKey(runtime, input);
|
|
683
|
-
const existing = await listVpnPayloads(context, target);
|
|
684
|
-
const sameKey = existing.find((payload) => text(vpnPeer(payload).public_key) === publicKey);
|
|
685
|
-
let effectiveName = name;
|
|
686
|
-
if (sameKey) {
|
|
687
|
-
const existingName = text(vpnPeer(sameKey).label) ?? "VPN connection";
|
|
688
|
-
effectiveName = existingName;
|
|
689
|
-
if (normalizedName(existingName) !== normalizedName(name)) {
|
|
690
|
-
runtime.usage("This public key already identifies a VPN connection with a different name. Use the original effective input or a new key after review.", { code: "vpn_connection_replay_conflict", field: "name" });
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
else if (existing.some((payload) => normalizedName(text(vpnPeer(payload).label) ?? "VPN connection") === normalizedName(name))) {
|
|
694
|
-
runtime.usage(`A VPN connection named "${name}" already exists with different key material.`, {
|
|
695
|
-
code: "vpn_connection_name_conflict",
|
|
696
|
-
field: "name",
|
|
697
|
-
});
|
|
698
|
-
}
|
|
699
|
-
if (sameKey && lifecycle(text(vpnPeer(sameKey).status)).class === "ready") {
|
|
700
|
-
const exported = await exportVpnConfig(runtime, input, sameKey.config, privateKeyPath, publicKey);
|
|
701
|
-
return vpnConfigureExecution(runtime, target, projectVpnConnection(sameKey, target), exported, true);
|
|
702
|
-
}
|
|
703
|
-
if (!(await runtime.review({
|
|
704
|
-
title: sameKey ? "Retry VPN connection" : "Configure VPN connection",
|
|
705
|
-
terms: [
|
|
706
|
-
{ label: "Private target", value: target },
|
|
707
|
-
{ label: "VPN connection", value: name },
|
|
708
|
-
{ label: "WireGuard public key", value: publicKey },
|
|
709
|
-
{
|
|
710
|
-
label: "Configuration destination",
|
|
711
|
-
value: input.print ? "Terminal (explicit --print)" : String(input.output),
|
|
712
|
-
},
|
|
713
|
-
],
|
|
714
|
-
})))
|
|
715
|
-
return declined(runtime);
|
|
716
|
-
const applied = record(await context.fetch(vpnPath(target), {
|
|
717
|
-
body: { label: effectiveName, public_key: publicKey },
|
|
718
|
-
headers: mutationHeaders(idempotencyKey),
|
|
719
|
-
method: "POST",
|
|
720
|
-
}));
|
|
721
|
-
const exported = await exportVpnConfig(runtime, input, applied.config, privateKeyPath, publicKey);
|
|
722
|
-
const connection = projectVpnConnection(applied, target);
|
|
723
|
-
return vpnConfigureExecution(runtime, target, connection, exported, Boolean(sameKey));
|
|
724
|
-
}
|
|
725
|
-
async function disconnectVpnConnection(context, input) {
|
|
726
|
-
const runtime = interaction(context);
|
|
727
|
-
const [rawTarget, reference] = exactPositionals(runtime, input.$positionals, 2, "Usage: ornn vpn-connections disconnect <canonical-reservation-id> <exact-name-or-id>");
|
|
728
|
-
const target = requireCanonicalTarget(runtime, rawTarget);
|
|
729
|
-
const idempotencyKey = requireMutationKey(runtime, input);
|
|
730
|
-
const matches = (await listVpnPayloads(context, target)).filter((payload) => {
|
|
731
|
-
const peer = vpnPeer(payload);
|
|
732
|
-
return CANONICAL_ID.test(reference)
|
|
733
|
-
? text(peer.id) === reference
|
|
734
|
-
: normalizedName(text(peer.label) ?? "VPN connection") === normalizedName(reference);
|
|
735
|
-
});
|
|
736
|
-
if (matches.length === 0)
|
|
737
|
-
runtime.error(`VPN connection not found: ${reference}.`, { code: "vpn_connection_not_found" });
|
|
738
|
-
if (matches.length > 1) {
|
|
739
|
-
runtime.usage(`More than one VPN connection has the exact name "${reference}"; use a canonical identifier.`, {
|
|
740
|
-
code: "vpn_connection_ambiguous",
|
|
741
|
-
field: "connection",
|
|
742
|
-
});
|
|
743
|
-
}
|
|
744
|
-
const connection = projectVpnConnection(matches[0], target);
|
|
745
|
-
if (!(await runtime.review({
|
|
746
|
-
title: "Disconnect VPN connection",
|
|
747
|
-
terms: [
|
|
748
|
-
{ label: "Private target", value: target },
|
|
749
|
-
{ label: "VPN connection", value: `${String(connection.name)} (${String(connection.id)})` },
|
|
750
|
-
{
|
|
751
|
-
label: "Consequence",
|
|
752
|
-
value: "Private access through this VPN connection ends; the private network remains.",
|
|
753
|
-
},
|
|
754
|
-
],
|
|
755
|
-
})))
|
|
756
|
-
return declined(runtime);
|
|
757
|
-
await context.fetch(`${vpnPath(target)}/${encodeURIComponent(String(connection.id))}`, {
|
|
758
|
-
headers: mutationHeaders(idempotencyKey),
|
|
759
|
-
method: "DELETE",
|
|
760
|
-
});
|
|
761
|
-
return runtime.result({
|
|
762
|
-
data: { disconnected: true, vpn_connection: connection },
|
|
763
|
-
outcome: { class: "ended", phase: "disconnected" },
|
|
764
|
-
presentation: {
|
|
765
|
-
lines: [`VPN connection disconnected: ${String(connection.name)} (${String(connection.id)})`],
|
|
766
|
-
},
|
|
767
|
-
});
|
|
768
|
-
}
|
|
769
|
-
function normalizedConnectAccessMode(value) {
|
|
770
|
-
const mode = text(value)?.toLowerCase();
|
|
771
|
-
return mode === "virtual-machine" ? "vm" : (mode ?? null);
|
|
772
|
-
}
|
|
773
|
-
function validateKnownConnectMode(runtime, input, mode) {
|
|
774
|
-
if (mode === "vm" || mode === "bare-metal" || mode === "slurm") {
|
|
775
|
-
if (input.output || input.overwrite) {
|
|
776
|
-
runtime.usage(`${mode === "slurm" ? "Slurm" : mode === "vm" ? "VM" : "Bare Metal"} connect uses native SSH and does not write an output file.`, {
|
|
777
|
-
code: mode === "slurm" ? "slurm_output_unsupported" : "direct_machine_output_unsupported",
|
|
778
|
-
field: input.output ? "output" : "overwrite",
|
|
779
|
-
});
|
|
780
|
-
}
|
|
781
|
-
return;
|
|
782
|
-
}
|
|
783
|
-
if (mode === "kubernetes" && (input.identityFile || input.user || input.print)) {
|
|
784
|
-
runtime.usage("Kubernetes connect writes a kubeconfig and does not accept SSH identity, user, or print options.", {
|
|
785
|
-
code: input.print ? "kubeconfig_output_required" : "kubernetes_ssh_options_unsupported",
|
|
786
|
-
field: input.identityFile ? "identityFile" : input.user ? "user" : "output",
|
|
787
|
-
});
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
function blockedPrivateTransport(runtime, lifecycleValue, action, data, reservationId) {
|
|
791
|
-
const state = record(lifecycleValue);
|
|
792
|
-
const outcome = {
|
|
793
|
-
class: text(state.class) ?? "pending",
|
|
794
|
-
phase: text(state.phase) ?? "configuring",
|
|
795
|
-
};
|
|
796
|
-
return runtime.result({
|
|
797
|
-
action,
|
|
798
|
-
data,
|
|
799
|
-
exitCode: ["failed", "ended"].includes(outcome.class) ? 1 : 3,
|
|
800
|
-
outcome,
|
|
801
|
-
resume: { kind: "connect", ref: reservationId },
|
|
802
|
-
});
|
|
803
|
-
}
|
|
804
|
-
function compareVpnConnections(left, right) {
|
|
805
|
-
return ((text(left.created_at) ?? "").localeCompare(text(right.created_at) ?? "") ||
|
|
806
|
-
(text(left.id) ?? "").localeCompare(text(right.id) ?? "") ||
|
|
807
|
-
normalizedName(text(left.name) ?? "").localeCompare(normalizedName(text(right.name) ?? "")));
|
|
808
|
-
}
|
|
809
|
-
async function connectPrivateTarget(context, input) {
|
|
810
|
-
const runtime = interaction(context);
|
|
811
|
-
const values = input.$positionals ?? [];
|
|
812
|
-
if (values.length > 1) {
|
|
813
|
-
runtime.usage("Usage: ornn connect [<exact-hostname-or-canonical-machine-id>]", {
|
|
814
|
-
code: "usage_error",
|
|
815
|
-
});
|
|
816
|
-
}
|
|
817
|
-
const rawTarget = text(values[0]);
|
|
818
|
-
let resolved = null;
|
|
819
|
-
let target = rawTarget;
|
|
820
|
-
if (runtime.resolveConnectTarget) {
|
|
821
|
-
resolved = await runtime.resolveConnectTarget({
|
|
822
|
-
context,
|
|
823
|
-
input,
|
|
824
|
-
interaction: runtime,
|
|
825
|
-
reference: rawTarget,
|
|
826
|
-
});
|
|
827
|
-
if (resolved) {
|
|
828
|
-
validateKnownConnectMode(runtime, input, normalizedConnectAccessMode(resolved.mode));
|
|
829
|
-
}
|
|
830
|
-
if (resolved && resolved.privateTransportRequired !== true && runtime.continueConnect) {
|
|
831
|
-
const direct = await runtime.continueConnect(resolved);
|
|
832
|
-
if (direct)
|
|
833
|
-
return runtime.result(direct);
|
|
834
|
-
}
|
|
835
|
-
if (resolved)
|
|
836
|
-
target = resolved.reservationId;
|
|
837
|
-
}
|
|
838
|
-
if (!resolved && rawTarget && CANONICAL_ID.test(rawTarget)) {
|
|
839
|
-
try {
|
|
840
|
-
const reservation = record(await context.fetch(`/v1/workflows/commerce/reservations/${encodeURIComponent(rawTarget)}`));
|
|
841
|
-
const reservationId = text(reservation.id);
|
|
842
|
-
if (reservationId && reservationId !== rawTarget) {
|
|
843
|
-
runtime.error("The gateway returned a different capacity reservation.", {
|
|
844
|
-
code: "reservation_correlation_changed",
|
|
845
|
-
});
|
|
846
|
-
}
|
|
847
|
-
const status = text(reservation.status)?.toLowerCase() ?? "unknown";
|
|
848
|
-
if (status !== "active") {
|
|
849
|
-
const state = reservationTargetLifecycle(status);
|
|
850
|
-
const waiting = state.class === "pending" || state.class === "working";
|
|
851
|
-
return runtime.result({
|
|
852
|
-
action: waiting
|
|
853
|
-
? {
|
|
854
|
-
command: `ornn reservations show ${reservationId ?? rawTarget}`,
|
|
855
|
-
description: "Wait for the capacity reservation to become active before launching native compute.",
|
|
856
|
-
type: "command",
|
|
857
|
-
}
|
|
858
|
-
: null,
|
|
859
|
-
data: {
|
|
860
|
-
capacity_reservation: {
|
|
861
|
-
id: reservationId ?? rawTarget,
|
|
862
|
-
access_mode: normalizedConnectAccessMode(reservation.access_mode ?? reservation.accessMode),
|
|
863
|
-
lifecycle: state,
|
|
864
|
-
},
|
|
865
|
-
},
|
|
866
|
-
exitCode: waiting ? 3 : 1,
|
|
867
|
-
outcome: state,
|
|
868
|
-
presentation: {
|
|
869
|
-
lines: [
|
|
870
|
-
waiting
|
|
871
|
-
? `Capacity reservation ${reservationId ?? rawTarget} is not active yet; it has no native-access target.`
|
|
872
|
-
: `Capacity reservation ${reservationId ?? rawTarget} is ${state.phase}; it has no launchable native-access target.`,
|
|
873
|
-
],
|
|
874
|
-
},
|
|
875
|
-
resume: waiting ? { kind: "reservation", ref: reservationId ?? rawTarget } : null,
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
const mode = normalizedConnectAccessMode(reservation.access_mode ?? reservation.accessMode);
|
|
879
|
-
validateKnownConnectMode(runtime, input, mode);
|
|
880
|
-
return runtime.actionRequired({
|
|
881
|
-
action: {
|
|
882
|
-
command: "ornn launch",
|
|
883
|
-
description: "Launch compute for this capacity reservation before requesting native access.",
|
|
884
|
-
type: "command",
|
|
885
|
-
},
|
|
886
|
-
data: {
|
|
887
|
-
capacity_reservation: {
|
|
888
|
-
id: reservationId ?? rawTarget,
|
|
889
|
-
access_mode: mode,
|
|
890
|
-
},
|
|
891
|
-
},
|
|
892
|
-
resume: { kind: "reservation", ref: reservationId ?? rawTarget },
|
|
893
|
-
});
|
|
894
|
-
}
|
|
895
|
-
catch (error) {
|
|
896
|
-
if (![400, 404, 422].includes(responseStatus(error) ?? 0))
|
|
897
|
-
throw error;
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
const privateTarget = target ?? "";
|
|
901
|
-
if (!privateTarget || !CANONICAL_ID.test(privateTarget)) {
|
|
902
|
-
runtime.usage("Connect requires an exact unique hostname, canonical machine ID, or eligible guided choice.", {
|
|
903
|
-
code: "invalid_reference",
|
|
904
|
-
field: "machine",
|
|
905
|
-
});
|
|
906
|
-
}
|
|
907
|
-
// A UUID and an organization network do not prove that this target exists.
|
|
908
|
-
// Only a resolved native target may continue into private transport setup.
|
|
909
|
-
if (!resolved) {
|
|
910
|
-
runtime.error("No accessible machine, cluster, or capacity reservation matches this target.", {
|
|
911
|
-
code: "connect_target_not_found",
|
|
912
|
-
});
|
|
913
|
-
}
|
|
914
|
-
const shared = record(await context.fetch(`${NETWORKS_PATH}/shared?overlay=true`));
|
|
915
|
-
const privateNetwork = projectNetwork({
|
|
916
|
-
...record(shared.network),
|
|
917
|
-
id: "shared",
|
|
918
|
-
name: "Organization private network",
|
|
919
|
-
});
|
|
920
|
-
const networkState = record(privateNetwork.lifecycle);
|
|
921
|
-
if (networkState.class !== "ready") {
|
|
922
|
-
const recoverable = ["failed", "ended"].includes(String(networkState.class));
|
|
923
|
-
return blockedPrivateTransport(runtime, networkState, {
|
|
924
|
-
command: `ornn vpn-connections list ${privateTarget}`,
|
|
925
|
-
description: recoverable
|
|
926
|
-
? "Inspect organization VPN connectivity before retrying."
|
|
927
|
-
: "Wait for organization networking to become ready, then retry connect.",
|
|
928
|
-
type: "command",
|
|
929
|
-
}, {
|
|
930
|
-
private_network: privateNetwork,
|
|
931
|
-
private_target: { reservation_id: privateTarget },
|
|
932
|
-
vpn_connection: null,
|
|
933
|
-
}, privateTarget);
|
|
934
|
-
}
|
|
935
|
-
const connections = (await listVpnPayloads(context, privateTarget))
|
|
936
|
-
.map((payload) => projectVpnConnection(payload, privateTarget))
|
|
937
|
-
.sort(compareVpnConnections);
|
|
938
|
-
if (connections.length === 0) {
|
|
939
|
-
return runtime.actionRequired({
|
|
940
|
-
action: {
|
|
941
|
-
command: `ornn vpn-connections configure ${privateTarget} --guided`,
|
|
942
|
-
description: "Provide one VPN peer and explicit configuration destination, then retry connect.",
|
|
943
|
-
type: "command",
|
|
944
|
-
},
|
|
945
|
-
data: {
|
|
946
|
-
private_network: privateNetwork,
|
|
947
|
-
private_target: { reservation_id: privateTarget },
|
|
948
|
-
vpn_connection: null,
|
|
949
|
-
},
|
|
950
|
-
resume: { kind: "connect", ref: privateTarget },
|
|
951
|
-
});
|
|
952
|
-
}
|
|
953
|
-
const readyConnections = connections.filter((connection) => record(connection.lifecycle).class === "ready");
|
|
954
|
-
if (readyConnections.length === 0) {
|
|
955
|
-
const connection = connections.find((candidate) => ["pending", "working"].includes(String(record(candidate.lifecycle).class))) ??
|
|
956
|
-
connections.find((candidate) => record(candidate.lifecycle).class === "failed") ??
|
|
957
|
-
connections[0];
|
|
958
|
-
const state = record(connection.lifecycle);
|
|
959
|
-
const stateClass = String(state.class);
|
|
960
|
-
const id = text(connection.id);
|
|
961
|
-
const failed = stateClass === "failed";
|
|
962
|
-
const ended = stateClass === "ended";
|
|
963
|
-
const action = failed && id
|
|
964
|
-
? {
|
|
965
|
-
command: `ornn vpn-connections disconnect ${privateTarget} ${id}`,
|
|
966
|
-
description: "Remove the failed VPN connection before configuring a replacement.",
|
|
967
|
-
type: "command",
|
|
968
|
-
}
|
|
969
|
-
: ended
|
|
970
|
-
? {
|
|
971
|
-
command: `ornn vpn-connections configure ${privateTarget} --guided`,
|
|
972
|
-
description: "Configure a new VPN connection, then retry connect.",
|
|
973
|
-
type: "command",
|
|
974
|
-
}
|
|
975
|
-
: {
|
|
976
|
-
command: `ornn vpn-connections configure ${privateTarget} --guided`,
|
|
977
|
-
description: "Reapply the pending VPN connection, then retry connect.",
|
|
978
|
-
type: "command",
|
|
979
|
-
};
|
|
980
|
-
return blockedPrivateTransport(runtime, state, action, {
|
|
981
|
-
private_network: privateNetwork,
|
|
982
|
-
private_target: { reservation_id: privateTarget },
|
|
983
|
-
vpn_connection: connection,
|
|
984
|
-
}, privateTarget);
|
|
985
|
-
}
|
|
986
|
-
const connection = readyConnections.find((candidate) => {
|
|
987
|
-
const address = text(candidate.assigned_address);
|
|
988
|
-
return Boolean(address && runtime.hasLocalNetworkAddress?.(address) === true);
|
|
989
|
-
}) ?? readyConnections[0];
|
|
990
|
-
const assignedAddress = text(connection.assigned_address);
|
|
991
|
-
if (!assignedAddress || runtime.hasLocalNetworkAddress?.(assignedAddress) !== true) {
|
|
992
|
-
return runtime.actionRequired({
|
|
993
|
-
action: {
|
|
994
|
-
command: `ornn vpn-connections configure ${privateTarget} --guided`,
|
|
995
|
-
description: "Export a complete WireGuard configuration, activate it locally, then retry connect.",
|
|
996
|
-
type: "command",
|
|
997
|
-
},
|
|
998
|
-
data: {
|
|
999
|
-
private_network: privateNetwork,
|
|
1000
|
-
private_target: { reservation_id: privateTarget },
|
|
1001
|
-
vpn_connection: connection,
|
|
1002
|
-
},
|
|
1003
|
-
resume: { kind: "connect", ref: privateTarget },
|
|
1004
|
-
});
|
|
1005
|
-
}
|
|
1006
|
-
if (runtime.continueConnect) {
|
|
1007
|
-
const continuation = await runtime.continueConnect({
|
|
1008
|
-
...(resolved ?? {}),
|
|
1009
|
-
privateNetwork,
|
|
1010
|
-
reservationId: privateTarget,
|
|
1011
|
-
vpnConnection: connection,
|
|
1012
|
-
});
|
|
1013
|
-
if (continuation)
|
|
1014
|
-
return runtime.result(continuation);
|
|
1015
|
-
}
|
|
1016
|
-
return runtime.result({
|
|
1017
|
-
data: {
|
|
1018
|
-
private_network: privateNetwork,
|
|
1019
|
-
private_target: { reservation_id: privateTarget },
|
|
1020
|
-
vpn_connection: connection,
|
|
1021
|
-
},
|
|
1022
|
-
outcome: { class: "ready", phase: "private_transport_ready" },
|
|
1023
|
-
presentation: {
|
|
1024
|
-
lines: ["VPN connection is ready for private access.", ...vpnLines(connection)],
|
|
1025
|
-
},
|
|
1026
|
-
});
|
|
1027
|
-
}
|
|
1028
|
-
function customerResource(specification) {
|
|
1029
|
-
return defineCapability({ ...specification, mcp: false, slack: false });
|
|
1030
|
-
}
|
|
1031
|
-
export const privateNetworkListCapability = customerResource({
|
|
1032
|
-
id: "networks.list",
|
|
1033
|
-
domain: "networks",
|
|
1034
|
-
roles: ["user"],
|
|
1035
|
-
mutation: "read",
|
|
1036
|
-
input: listInput,
|
|
1037
|
-
description: "List customer private networks with bounded pagination.",
|
|
1038
|
-
cli: {
|
|
1039
|
-
command: ["private-networks", "list"],
|
|
1040
|
-
vnext: {
|
|
1041
|
-
arguments: "semantic",
|
|
1042
|
-
audience: "customer",
|
|
1043
|
-
category: "resource",
|
|
1044
|
-
order: 500,
|
|
1045
|
-
outcome: "List private networks without exposing owner or supplier details.",
|
|
1046
|
-
human: "ornn private-networks list [--limit <1-500>] [--cursor <cursor>]",
|
|
1047
|
-
automation: "ornn private-networks list --format json [--limit <1-500>] [--cursor <cursor>]",
|
|
1048
|
-
related: ["private-networks show"],
|
|
1049
|
-
},
|
|
1050
|
-
},
|
|
1051
|
-
execute: listPrivateNetworks,
|
|
1052
|
-
});
|
|
1053
|
-
export const privateNetworkShowCapability = customerResource({
|
|
1054
|
-
id: "networks.show",
|
|
1055
|
-
domain: "networks",
|
|
1056
|
-
roles: ["user"],
|
|
1057
|
-
mutation: "read",
|
|
1058
|
-
input: noOptionsInput,
|
|
1059
|
-
description: "Show one exact customer private network.",
|
|
1060
|
-
cli: {
|
|
1061
|
-
command: ["private-networks", "show"],
|
|
1062
|
-
vnext: {
|
|
1063
|
-
arguments: "semantic",
|
|
1064
|
-
audience: "customer",
|
|
1065
|
-
category: "resource",
|
|
1066
|
-
order: 501,
|
|
1067
|
-
outcome: "Inspect one exact private network and its readiness.",
|
|
1068
|
-
human: "ornn private-networks show <exact-name-or-id>",
|
|
1069
|
-
automation: "ornn private-networks show <exact-name-or-id> --format json",
|
|
1070
|
-
acceptedReferences: ["canonical private-network ID", "exact unique private-network name"],
|
|
1071
|
-
related: ["private-networks list"],
|
|
1072
|
-
},
|
|
1073
|
-
},
|
|
1074
|
-
execute: showPrivateNetwork,
|
|
1075
|
-
});
|
|
1076
|
-
export const privateNetworkCreateCapability = customerResource({
|
|
1077
|
-
id: "networks.create",
|
|
1078
|
-
domain: "networks",
|
|
1079
|
-
roles: ["user"],
|
|
1080
|
-
mutation: "preview-confirm",
|
|
1081
|
-
input: networkCreateInput,
|
|
1082
|
-
description: "Create or safely replay a named customer private network.",
|
|
1083
|
-
cli: {
|
|
1084
|
-
command: ["private-networks", "create"],
|
|
1085
|
-
vnext: {
|
|
1086
|
-
arguments: "semantic",
|
|
1087
|
-
audience: "customer",
|
|
1088
|
-
category: "resource",
|
|
1089
|
-
order: 502,
|
|
1090
|
-
outcome: "Create one reviewed private network with name-based durable replay.",
|
|
1091
|
-
human: "ornn private-networks create --name <name> [--cidr <cidr>]",
|
|
1092
|
-
automation: "ornn private-networks create --name <name> [--cidr <cidr>] --idempotency-key <key> --format json",
|
|
1093
|
-
review: "Review the normalized name and CIDR before creation.",
|
|
1094
|
-
resumableStates: ["ready"],
|
|
1095
|
-
related: ["private-networks list"],
|
|
1096
|
-
},
|
|
1097
|
-
},
|
|
1098
|
-
execute: createPrivateNetwork,
|
|
1099
|
-
});
|
|
1100
|
-
export const privateNetworkUpdateCapability = customerResource({
|
|
1101
|
-
id: "networks.update",
|
|
1102
|
-
domain: "networks",
|
|
1103
|
-
roles: ["user"],
|
|
1104
|
-
mutation: "preview-confirm",
|
|
1105
|
-
input: networkUpdateInput,
|
|
1106
|
-
description: "Rename one exact customer private network.",
|
|
1107
|
-
cli: {
|
|
1108
|
-
command: ["private-networks", "update"],
|
|
1109
|
-
vnext: {
|
|
1110
|
-
arguments: "semantic",
|
|
1111
|
-
audience: "customer",
|
|
1112
|
-
category: "resource",
|
|
1113
|
-
order: 503,
|
|
1114
|
-
outcome: "Apply one reviewed private-network name goal.",
|
|
1115
|
-
human: "ornn private-networks update <exact-name-or-id> --name <new-name>",
|
|
1116
|
-
automation: "ornn private-networks update <exact-name-or-id> --name <new-name> --idempotency-key <key> --format json",
|
|
1117
|
-
acceptedReferences: ["canonical private-network ID", "exact unique private-network name"],
|
|
1118
|
-
review: "Review the exact private network and normalized new name.",
|
|
1119
|
-
related: ["private-networks show"],
|
|
1120
|
-
},
|
|
1121
|
-
},
|
|
1122
|
-
execute: updatePrivateNetwork,
|
|
1123
|
-
});
|
|
1124
|
-
export const privateNetworkDeleteCapability = customerResource({
|
|
1125
|
-
id: "networks.delete",
|
|
1126
|
-
domain: "networks",
|
|
1127
|
-
roles: ["user"],
|
|
1128
|
-
mutation: "preview-confirm",
|
|
1129
|
-
input: noOptionsInput,
|
|
1130
|
-
description: "Delete an unattached customer private network.",
|
|
1131
|
-
cli: {
|
|
1132
|
-
command: ["private-networks", "delete"],
|
|
1133
|
-
vnext: {
|
|
1134
|
-
arguments: "semantic",
|
|
1135
|
-
audience: "customer",
|
|
1136
|
-
category: "resource",
|
|
1137
|
-
order: 504,
|
|
1138
|
-
outcome: "Delete a private network when the owner permits deletion.",
|
|
1139
|
-
human: "ornn private-networks delete <exact-name-or-id>",
|
|
1140
|
-
automation: "ornn private-networks delete <exact-name-or-id> --idempotency-key <key> --format json",
|
|
1141
|
-
acceptedReferences: ["canonical private-network ID", "exact unique private-network name"],
|
|
1142
|
-
review: "Review the exact organization network before permanent deletion.",
|
|
1143
|
-
related: ["private-networks list"],
|
|
1144
|
-
},
|
|
1145
|
-
},
|
|
1146
|
-
execute: deletePrivateNetwork,
|
|
1147
|
-
});
|
|
1148
|
-
export const vpnConnectionListCapability = customerResource({
|
|
1149
|
-
id: "vpn.connections.list",
|
|
1150
|
-
domain: "vpn",
|
|
1151
|
-
roles: ["user"],
|
|
1152
|
-
mutation: "read",
|
|
1153
|
-
input: listInput,
|
|
1154
|
-
description: "List named VPN connections for one exact private target.",
|
|
1155
|
-
cli: {
|
|
1156
|
-
command: ["vpn-connections", "list"],
|
|
1157
|
-
vnext: {
|
|
1158
|
-
arguments: "semantic",
|
|
1159
|
-
audience: "customer",
|
|
1160
|
-
category: "resource",
|
|
1161
|
-
order: 520,
|
|
1162
|
-
outcome: "List safe VPN-connection metadata without configuration bytes or backend terminology.",
|
|
1163
|
-
human: "ornn vpn-connections list <canonical-reservation-id> [--limit <1-500>] [--cursor <cursor>]",
|
|
1164
|
-
automation: "ornn vpn-connections list <canonical-reservation-id> --format json [--limit <1-500>] [--cursor <cursor>]",
|
|
1165
|
-
acceptedReferences: ["canonical capacity-reservation ID"],
|
|
1166
|
-
related: ["vpn-connections configure", "connect"],
|
|
1167
|
-
},
|
|
1168
|
-
},
|
|
1169
|
-
execute: listVpnConnections,
|
|
1170
|
-
});
|
|
1171
|
-
export const vpnConnectionConfigureCapability = customerResource({
|
|
1172
|
-
id: "vpn.connections.configure",
|
|
1173
|
-
domain: "vpn",
|
|
1174
|
-
roles: ["user"],
|
|
1175
|
-
mutation: "preview-confirm",
|
|
1176
|
-
input: vpnConfigureInput,
|
|
1177
|
-
description: "Configure and explicitly export one named VPN connection.",
|
|
1178
|
-
cli: {
|
|
1179
|
-
command: ["vpn-connections", "configure"],
|
|
1180
|
-
vnext: {
|
|
1181
|
-
arguments: "semantic",
|
|
1182
|
-
audience: "customer",
|
|
1183
|
-
category: "resource",
|
|
1184
|
-
order: 521,
|
|
1185
|
-
outcome: "Configure or replay one VPN connection and explicitly export its secret configuration.",
|
|
1186
|
-
human: "ornn vpn-connections configure <canonical-reservation-id> --name <name> --private-key <path> (--print | --output <path>)",
|
|
1187
|
-
automation: "ornn vpn-connections configure <canonical-reservation-id> --name <name> --private-key <path> --output <path> --idempotency-key <key> --format json",
|
|
1188
|
-
acceptedReferences: ["canonical capacity-reservation ID"],
|
|
1189
|
-
review: "Review the exact private target, locally derived WireGuard public key, connection name, and explicit secret destination.",
|
|
1190
|
-
resumableStates: ["ready"],
|
|
1191
|
-
related: ["connect", "vpn-connections disconnect"],
|
|
1192
|
-
},
|
|
1193
|
-
},
|
|
1194
|
-
execute: configureVpnConnection,
|
|
1195
|
-
});
|
|
1196
|
-
export const vpnConnectionDisconnectCapability = customerResource({
|
|
1197
|
-
id: "vpn.connections.disconnect",
|
|
1198
|
-
domain: "vpn",
|
|
1199
|
-
roles: ["user"],
|
|
1200
|
-
mutation: "preview-confirm",
|
|
1201
|
-
input: noOptionsInput,
|
|
1202
|
-
description: "Disconnect one exact named VPN connection.",
|
|
1203
|
-
cli: {
|
|
1204
|
-
command: ["vpn-connections", "disconnect"],
|
|
1205
|
-
vnext: {
|
|
1206
|
-
arguments: "semantic",
|
|
1207
|
-
audience: "customer",
|
|
1208
|
-
category: "resource",
|
|
1209
|
-
order: 522,
|
|
1210
|
-
outcome: "Disconnect one VPN connection without deleting its private network.",
|
|
1211
|
-
human: "ornn vpn-connections disconnect <canonical-reservation-id> <exact-name-or-id>",
|
|
1212
|
-
automation: "ornn vpn-connections disconnect <canonical-reservation-id> <exact-name-or-id> --idempotency-key <key> --format json",
|
|
1213
|
-
acceptedReferences: [
|
|
1214
|
-
"canonical capacity-reservation ID",
|
|
1215
|
-
"canonical or exact unique VPN-connection reference",
|
|
1216
|
-
],
|
|
1217
|
-
review: "Review the exact VPN connection before ending private access.",
|
|
1218
|
-
resumableStates: ["ended"],
|
|
1219
|
-
related: ["vpn-connections configure"],
|
|
1220
|
-
},
|
|
1221
|
-
},
|
|
1222
|
-
execute: disconnectVpnConnection,
|
|
1223
|
-
});
|
|
1224
|
-
export const connectCapability = customerResource({
|
|
1225
|
-
id: "vpn.connect",
|
|
1226
|
-
domain: "vpn",
|
|
1227
|
-
roles: ["user"],
|
|
1228
|
-
mutation: "read",
|
|
1229
|
-
input: connectInput,
|
|
1230
|
-
description: "Connect to one exact native machine or cluster target through recoverable private transport when required.",
|
|
1231
|
-
cli: {
|
|
1232
|
-
command: ["connect"],
|
|
1233
|
-
vnext: {
|
|
1234
|
-
arguments: "semantic",
|
|
1235
|
-
audience: "customer",
|
|
1236
|
-
category: "journey",
|
|
1237
|
-
order: 80,
|
|
1238
|
-
outcome: "Open or render native access from exact machine, cluster, or reservation facts.",
|
|
1239
|
-
human: "ornn connect [<exact-hostname-or-canonical-machine-id-or-cluster-id-or-reservation-id>] [--identity-file <path>] [--user <login>] [--output <path>] [--print] [--overwrite]",
|
|
1240
|
-
automation: "ornn connect <canonical-machine-or-cluster-id> [--identity-file <path>] [--user <login>] [--output <path>] [--print] [--overwrite] --format json",
|
|
1241
|
-
acceptedReferences: [
|
|
1242
|
-
"canonical machine ID",
|
|
1243
|
-
"canonical cluster ID",
|
|
1244
|
-
"exact unique hostname",
|
|
1245
|
-
"canonical capacity-reservation ID",
|
|
1246
|
-
"invocation-local cluster choice from reservation, location, access mode, hardware, and node facts",
|
|
1247
|
-
],
|
|
1248
|
-
resumableStates: ["action_required", "working", "ready"],
|
|
1249
|
-
related: ["launch", "vpn-connections configure", "clusters show"],
|
|
1250
|
-
},
|
|
1251
|
-
},
|
|
1252
|
-
execute: connectPrivateTarget,
|
|
1253
|
-
});
|
|
1254
|
-
export const networkCapabilities = [
|
|
1255
|
-
privateNetworkListCapability,
|
|
1256
|
-
privateNetworkShowCapability,
|
|
1257
|
-
privateNetworkCreateCapability,
|
|
1258
|
-
privateNetworkUpdateCapability,
|
|
1259
|
-
privateNetworkDeleteCapability,
|
|
1260
|
-
vpnConnectionListCapability,
|
|
1261
|
-
vpnConnectionConfigureCapability,
|
|
1262
|
-
vpnConnectionDisconnectCapability,
|
|
1263
|
-
connectCapability,
|
|
1264
|
-
];
|