@ornncompute/cli 0.2.23 → 1.0.25
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 +304 -420
- package/package.json +12 -6
- package/src/admin-capacity-reservations.mjs +280 -0
- package/src/admin-command-handlers.mjs +57 -0
- package/src/admin-enrollment.mjs +680 -0
- package/src/admin-mutations.mjs +187 -0
- package/src/admin-node-diagnostics.mjs +412 -0
- package/src/admin-node-lifecycle.mjs +908 -0
- package/src/admin-shell.mjs +322 -0
- package/src/api-client.mjs +81 -28
- package/src/atomic-output.mjs +112 -0
- package/src/auth-store.mjs +182 -11
- package/src/capability-catalog.mjs +7 -0
- package/src/catalog-shell.mjs +1350 -0
- package/src/cli.mjs +123 -10266
- package/src/connect-mode-handlers.mjs +170 -0
- package/src/device-auth.mjs +178 -61
- package/src/evidence-commands.mjs +1340 -0
- package/src/external-bucket-commands.mjs +1374 -0
- package/src/interaction-runtime.mjs +310 -0
- package/src/kubernetes-commands.mjs +2229 -0
- package/src/launch-mode-handlers.mjs +119 -0
- package/src/machine-reference.mjs +277 -0
- package/src/mount-commands.mjs +1146 -0
- package/src/session-commands.mjs +565 -0
- package/src/slurm-commands.mjs +263 -0
- package/src/startup.mjs +34 -11
- package/src/storage-commands.mjs +1144 -0
- package/src/update.mjs +172 -24
- package/src/vm-commands.mjs +4273 -0
- package/src/vpn-secret-output.mjs +150 -0
- package/src/wait-runtime.mjs +231 -0
- package/vendor/capabilities/capabilities/admin-capacity-reservations.js +126 -0
- package/vendor/capabilities/capabilities/admin-enrollment.js +321 -0
- package/vendor/capabilities/capabilities/admin-node-diagnostics.js +88 -0
- package/vendor/capabilities/capabilities/admin-nodes-lifecycle.js +235 -0
- package/vendor/capabilities/capabilities/admin.js +89 -0
- package/vendor/capabilities/capabilities/capacity-offers.js +368 -0
- package/vendor/capabilities/capabilities/cli.js +156 -0
- package/vendor/capabilities/capabilities/clusters.js +150 -1
- package/vendor/capabilities/capabilities/compute.js +522 -0
- package/vendor/capabilities/capabilities/evidence.js +145 -0
- package/vendor/capabilities/capabilities/external-buckets.js +191 -0
- package/vendor/capabilities/capabilities/fleet.js +21 -5
- package/vendor/capabilities/capabilities/identity.js +114 -3
- package/vendor/capabilities/capabilities/listings.js +14 -8
- package/vendor/capabilities/capabilities/mounts.js +176 -0
- package/vendor/capabilities/capabilities/networks.js +1257 -0
- package/vendor/capabilities/capabilities/observability.js +38 -8
- package/vendor/capabilities/capabilities/tokens.js +20 -8
- package/vendor/capabilities/capabilities/volumes.js +178 -0
- package/vendor/capabilities/capability.js +19 -13
- package/vendor/capabilities/cli-catalog.js +65 -0
- package/vendor/capabilities/cli-migrations.js +288 -0
- package/vendor/capabilities/cli.js +7 -0
- package/vendor/capabilities/software-catalog.js +36 -0
- package/vendor/capabilities/spec-catalog.js +2 -2
- package/src/catalog-dispatch.mjs +0 -605
- package/src/color.mjs +0 -71
- package/vendor/capabilities/capabilities/clusters.d.ts +0 -2
- package/vendor/capabilities/capabilities/fleet.d.ts +0 -8
- package/vendor/capabilities/capabilities/identity.d.ts +0 -3
- package/vendor/capabilities/capabilities/listings.d.ts +0 -51
- package/vendor/capabilities/capabilities/observability.d.ts +0 -5
- package/vendor/capabilities/capabilities/staff-access.d.ts +0 -29
- package/vendor/capabilities/capabilities/staff-access.js +0 -278
- package/vendor/capabilities/capabilities/staff-commerce.d.ts +0 -160
- package/vendor/capabilities/capabilities/staff-commerce.js +0 -689
- package/vendor/capabilities/capabilities/staff-fleet.d.ts +0 -140
- package/vendor/capabilities/capabilities/staff-fleet.js +0 -538
- package/vendor/capabilities/capabilities/staff-gtm.d.ts +0 -142
- package/vendor/capabilities/capabilities/staff-gtm.js +0 -521
- package/vendor/capabilities/capabilities/staff-shared.d.ts +0 -47
- package/vendor/capabilities/capabilities/staff-shared.js +0 -98
- package/vendor/capabilities/capabilities/tokens.d.ts +0 -10
- package/vendor/capabilities/capabilities/users.d.ts +0 -2
- package/vendor/capabilities/capability.d.ts +0 -93
- package/vendor/capabilities/catalog.d.ts +0 -3
- package/vendor/capabilities/catalog.js +0 -26
- package/vendor/capabilities/index.d.ts +0 -16
- package/vendor/capabilities/index.js +0 -16
- package/vendor/capabilities/names.d.ts +0 -6
- package/vendor/capabilities/role.d.ts +0 -6
- package/vendor/capabilities/role.js +0 -17
- package/vendor/capabilities/spec-catalog.d.ts +0 -78
|
@@ -0,0 +1,1350 @@
|
|
|
1
|
+
import { HARDWARE_CATALOG_PATH } from "../vendor/capabilities/cli.js";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CliApiError,
|
|
5
|
+
cliRequest,
|
|
6
|
+
operatorRequest,
|
|
7
|
+
resolveApiBaseUrl,
|
|
8
|
+
resolveInstallerBaseUrl,
|
|
9
|
+
} from "./api-client.mjs";
|
|
10
|
+
import {
|
|
11
|
+
clearPendingWorkspace,
|
|
12
|
+
loadAuthSession,
|
|
13
|
+
loadPendingWorkspace,
|
|
14
|
+
savePendingWorkspace,
|
|
15
|
+
} from "./auth-store.mjs";
|
|
16
|
+
import {
|
|
17
|
+
AdminUsageError,
|
|
18
|
+
adminCommandData,
|
|
19
|
+
adminRootData,
|
|
20
|
+
finalizeAdminResult,
|
|
21
|
+
resolveAdminRequest,
|
|
22
|
+
writeAdminHelpHuman,
|
|
23
|
+
writeAdminResultHuman,
|
|
24
|
+
writeAdminRootHuman,
|
|
25
|
+
} from "./admin-shell.mjs";
|
|
26
|
+
import { adminCommandName, preflightAdminCommand } from "./admin-command-handlers.mjs";
|
|
27
|
+
import { AdminMutationError } from "./admin-mutations.mjs";
|
|
28
|
+
import { loadCapabilities } from "./capability-catalog.mjs";
|
|
29
|
+
import {
|
|
30
|
+
continueConnectWithHandlers,
|
|
31
|
+
resolveConnectTargetWithHandlers,
|
|
32
|
+
} from "./connect-mode-handlers.mjs";
|
|
33
|
+
import { dispatchLaunchWithHandlers } from "./launch-mode-handlers.mjs";
|
|
34
|
+
import { updateCommand } from "./update.mjs";
|
|
35
|
+
import {
|
|
36
|
+
executeExternalBucketCommand,
|
|
37
|
+
writeExternalBucketResultHuman,
|
|
38
|
+
} from "./external-bucket-commands.mjs";
|
|
39
|
+
import { executeEvidenceCommand } from "./evidence-commands.mjs";
|
|
40
|
+
import {
|
|
41
|
+
executeKubernetesClusterCommand,
|
|
42
|
+
kubernetesConnectModeHandler,
|
|
43
|
+
kubernetesLaunchModeHandler,
|
|
44
|
+
} from "./kubernetes-commands.mjs";
|
|
45
|
+
import {
|
|
46
|
+
executeSessionCommand,
|
|
47
|
+
parseSessionCommandInput,
|
|
48
|
+
SessionCommandError,
|
|
49
|
+
} from "./session-commands.mjs";
|
|
50
|
+
import {
|
|
51
|
+
createInteractionRuntime,
|
|
52
|
+
InteractionError,
|
|
53
|
+
isInteractionExecution,
|
|
54
|
+
parseSemanticArguments,
|
|
55
|
+
} from "./interaction-runtime.mjs";
|
|
56
|
+
import { executeMountCommand } from "./mount-commands.mjs";
|
|
57
|
+
import {
|
|
58
|
+
executeStorageCommand,
|
|
59
|
+
StorageCommandError,
|
|
60
|
+
writeStorageResultHuman,
|
|
61
|
+
} from "./storage-commands.mjs";
|
|
62
|
+
import {
|
|
63
|
+
exportVpnSecret,
|
|
64
|
+
hasLocalNetworkAddress,
|
|
65
|
+
prepareVpnSecretExport,
|
|
66
|
+
wireGuardPublicKeyFromFile,
|
|
67
|
+
} from "./vpn-secret-output.mjs";
|
|
68
|
+
import { createSlurmConnectModeHandler, slurmLaunchModeHandler } from "./slurm-commands.mjs";
|
|
69
|
+
import {
|
|
70
|
+
bareMetalLaunchModeHandler,
|
|
71
|
+
createBareMetalConnectModeHandler,
|
|
72
|
+
createVmConnectModeHandler,
|
|
73
|
+
executeMachineCommand,
|
|
74
|
+
executeSshKeyCommand,
|
|
75
|
+
vmLaunchModeHandler,
|
|
76
|
+
} from "./vm-commands.mjs";
|
|
77
|
+
import { waitForGoal } from "./wait-runtime.mjs";
|
|
78
|
+
|
|
79
|
+
const MACHINE_SCHEMA = "ornn.cli/v1";
|
|
80
|
+
const UPDATE_CHANNEL = "stable";
|
|
81
|
+
const FORMATS = { human: true, json: true, ndjson: true };
|
|
82
|
+
const SESSION_CAPABILITY_IDS = new Set(["identity.context", "identity.login", "identity.logout"]);
|
|
83
|
+
export const CLI_EXIT_STATUS = Object.freeze({
|
|
84
|
+
success: 0,
|
|
85
|
+
failure: 1,
|
|
86
|
+
usage: 2,
|
|
87
|
+
actionRequired: 3,
|
|
88
|
+
partial: 4,
|
|
89
|
+
authorization: 5,
|
|
90
|
+
unavailable: 6,
|
|
91
|
+
timeout: 124,
|
|
92
|
+
interrupted: 130,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
class VnextUsageError extends Error {
|
|
96
|
+
constructor(message, { code = "usage_error", field } = {}) {
|
|
97
|
+
super(message);
|
|
98
|
+
this.name = "VnextUsageError";
|
|
99
|
+
this.code = code;
|
|
100
|
+
this.field = field;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
class VnextContractError extends Error {
|
|
105
|
+
constructor(message, code) {
|
|
106
|
+
super(message);
|
|
107
|
+
this.name = "VnextContractError";
|
|
108
|
+
this.code = code;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function extractFormat(argv) {
|
|
113
|
+
const remaining = [];
|
|
114
|
+
let format;
|
|
115
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
116
|
+
const argument = argv[index];
|
|
117
|
+
if (argument === "--format" || argument.startsWith("--format=")) {
|
|
118
|
+
if (format !== undefined) {
|
|
119
|
+
return {
|
|
120
|
+
error: new VnextUsageError("--format may be supplied only once.", { field: "format" }),
|
|
121
|
+
format,
|
|
122
|
+
remaining,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const inline = argument.startsWith("--format=") ? argument.slice("--format=".length) : null;
|
|
126
|
+
const value = inline ?? argv[index + 1];
|
|
127
|
+
if (!value || (!inline && value.startsWith("-"))) {
|
|
128
|
+
return {
|
|
129
|
+
error: new VnextUsageError("--format requires human, json, or ndjson.", {
|
|
130
|
+
field: "format",
|
|
131
|
+
}),
|
|
132
|
+
format,
|
|
133
|
+
remaining,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (inline === null) index += 1;
|
|
137
|
+
format = value;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
remaining.push(argument);
|
|
141
|
+
}
|
|
142
|
+
if (format !== undefined && FORMATS[format] !== true) {
|
|
143
|
+
return {
|
|
144
|
+
error: new VnextUsageError(`Unsupported format: ${format}. Choose human, json, or ndjson.`, {
|
|
145
|
+
field: "format",
|
|
146
|
+
}),
|
|
147
|
+
format,
|
|
148
|
+
remaining,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return { error: null, format, remaining };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function vnextCommand(capability) {
|
|
155
|
+
return capability.cli?.vnext?.command ?? capability.cli?.command ?? [];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function customerCommands(catalog) {
|
|
159
|
+
return catalog
|
|
160
|
+
.filter((capability) => capability.cli?.vnext?.audience === "customer")
|
|
161
|
+
.sort((left, right) => {
|
|
162
|
+
const leftOrder = left.cli.vnext.order ?? Number.MAX_SAFE_INTEGER;
|
|
163
|
+
const rightOrder = right.cli.vnext.order ?? Number.MAX_SAFE_INTEGER;
|
|
164
|
+
return (
|
|
165
|
+
leftOrder - rightOrder ||
|
|
166
|
+
vnextCommand(left).join(" ").localeCompare(vnextCommand(right).join(" "))
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function commandMatch(catalog, argv) {
|
|
172
|
+
return customerCommands(catalog)
|
|
173
|
+
.filter((capability) => vnextCommand(capability).every((part, index) => argv[index] === part))
|
|
174
|
+
.sort((left, right) => vnextCommand(right).length - vnextCommand(left).length)[0];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function editDistance(left, right) {
|
|
178
|
+
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
179
|
+
for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
|
|
180
|
+
const current = [leftIndex];
|
|
181
|
+
for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1) {
|
|
182
|
+
current[rightIndex] = Math.min(
|
|
183
|
+
current[rightIndex - 1] + 1,
|
|
184
|
+
previous[rightIndex] + 1,
|
|
185
|
+
previous[rightIndex - 1] + (left[leftIndex - 1] === right[rightIndex - 1] ? 0 : 1)
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
previous.splice(0, previous.length, ...current);
|
|
189
|
+
}
|
|
190
|
+
return previous[right.length];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function suggestedCommand(catalog, input) {
|
|
194
|
+
if (!input || input.startsWith("-")) return null;
|
|
195
|
+
const roots = [
|
|
196
|
+
...new Set(customerCommands(catalog).map((capability) => vnextCommand(capability)[0])),
|
|
197
|
+
];
|
|
198
|
+
const candidates = roots
|
|
199
|
+
.map((command) => ({ command, distance: editDistance(input, command) }))
|
|
200
|
+
.sort(
|
|
201
|
+
(left, right) => left.distance - right.distance || left.command.localeCompare(right.command)
|
|
202
|
+
);
|
|
203
|
+
return candidates[0]?.distance <= 2 ? candidates[0].command : null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function commandGroup(catalog, path) {
|
|
207
|
+
const matches = customerCommands(catalog).filter((capability) => {
|
|
208
|
+
const command = vnextCommand(capability);
|
|
209
|
+
return path.length < command.length && path.every((part, index) => command[index] === part);
|
|
210
|
+
});
|
|
211
|
+
return matches.length ? matches : null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function retiredSyntaxError(argv, findLegacyCliMigration, migrationGuideUrl) {
|
|
215
|
+
if (argv.includes("--json")) {
|
|
216
|
+
const replacement = argv
|
|
217
|
+
.map((argument) => (argument === "--json" ? "--format json" : argument))
|
|
218
|
+
.join(" ");
|
|
219
|
+
return new VnextUsageError(
|
|
220
|
+
`Retired syntax: --json. Use \`ornn ${replacement}\`. See ${migrationGuideUrl}`,
|
|
221
|
+
{ code: "legacy_command_removed", field: "format" }
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
if (argv.includes("--interactive")) {
|
|
225
|
+
const replacement = argv
|
|
226
|
+
.map((argument) => (argument === "--interactive" ? "--guided" : argument))
|
|
227
|
+
.join(" ");
|
|
228
|
+
return new VnextUsageError(
|
|
229
|
+
`Retired syntax: --interactive. Use \`ornn ${replacement}\`. See ${migrationGuideUrl}`,
|
|
230
|
+
{ code: "legacy_command_removed", field: "guided" }
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
const migration = findLegacyCliMigration?.(argv);
|
|
234
|
+
if (!migration) return null;
|
|
235
|
+
return new VnextUsageError(
|
|
236
|
+
`Retired command: ornn ${migration.legacy.join(" ")}. Use \`${migration.replacement}\`. See ${migrationGuideUrl}`,
|
|
237
|
+
{ code: "legacy_command_removed" }
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function resolveRequest(catalog, argv, findLegacyCliMigration, migrationGuideUrl) {
|
|
242
|
+
if (argv.length === 0 || (argv.length === 1 && (argv[0] === "--help" || argv[0] === "-h"))) {
|
|
243
|
+
return { kind: "root", command: "help" };
|
|
244
|
+
}
|
|
245
|
+
if (argv[0] === "--help" || argv[0] === "-h") {
|
|
246
|
+
return {
|
|
247
|
+
kind: "error",
|
|
248
|
+
command: "help",
|
|
249
|
+
error: new VnextUsageError("Usage: ornn [--format human|json|ndjson]"),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const retired = retiredSyntaxError(argv, findLegacyCliMigration, migrationGuideUrl);
|
|
254
|
+
if (retired) {
|
|
255
|
+
return { kind: "error", command: argv.slice(0, 2).join(" ") || "help", error: retired };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (argv[0] === "admin") {
|
|
259
|
+
return { kind: "admin", command: "admin", argv: argv.slice(1) };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (argv[0] === "help") {
|
|
263
|
+
if (argv.length === 1) return { kind: "root", command: "help" };
|
|
264
|
+
if (argv[1] === "admin") {
|
|
265
|
+
return { kind: "admin", command: "admin", argv: ["help", ...argv.slice(2)] };
|
|
266
|
+
}
|
|
267
|
+
const targetArgv = argv.slice(1);
|
|
268
|
+
const target = commandMatch(catalog, targetArgv);
|
|
269
|
+
if (target && targetArgv.length === vnextCommand(target).length) {
|
|
270
|
+
return {
|
|
271
|
+
kind: "help",
|
|
272
|
+
command: `help ${vnextCommand(target).join(" ")}`,
|
|
273
|
+
capability: target,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
const group = commandGroup(catalog, targetArgv);
|
|
277
|
+
if (group) {
|
|
278
|
+
return {
|
|
279
|
+
kind: "help-group",
|
|
280
|
+
command: `help ${targetArgv.join(" ")}`,
|
|
281
|
+
path: targetArgv,
|
|
282
|
+
capabilities: group,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
const suggestion = suggestedCommand(catalog, argv[1]);
|
|
286
|
+
return {
|
|
287
|
+
kind: "error",
|
|
288
|
+
command: `help ${argv[1]}`,
|
|
289
|
+
error: new VnextUsageError(
|
|
290
|
+
suggestion
|
|
291
|
+
? `Unknown command: ${argv[1]}. Did you mean \`ornn help ${suggestion}\`?`
|
|
292
|
+
: `Unknown command: ${targetArgv.join(" ")}. Run \`ornn help\` to list registered commands.`,
|
|
293
|
+
{ code: "unknown_command" }
|
|
294
|
+
),
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const trailingHelp = argv.at(-1) === "--help" || argv.at(-1) === "-h";
|
|
299
|
+
if (trailingHelp) {
|
|
300
|
+
const targetArgv = argv.slice(0, -1);
|
|
301
|
+
const group = commandGroup(catalog, targetArgv);
|
|
302
|
+
if (group) {
|
|
303
|
+
return {
|
|
304
|
+
kind: "help-group",
|
|
305
|
+
command: `help ${targetArgv.join(" ")}`,
|
|
306
|
+
path: targetArgv,
|
|
307
|
+
capabilities: group,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const capability = commandMatch(catalog, argv);
|
|
313
|
+
if (capability) {
|
|
314
|
+
const command = vnextCommand(capability);
|
|
315
|
+
const rest = argv.slice(command.length);
|
|
316
|
+
const helpFlags = rest.filter((argument) => argument === "--help" || argument === "-h");
|
|
317
|
+
if (helpFlags.length) {
|
|
318
|
+
if (rest.length !== 1) {
|
|
319
|
+
return {
|
|
320
|
+
kind: "error",
|
|
321
|
+
command: command.join(" "),
|
|
322
|
+
error: new VnextUsageError(`Usage: ${capability.cli.vnext.human}`),
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
return { kind: "help", command: `help ${command.join(" ")}`, capability };
|
|
326
|
+
}
|
|
327
|
+
if (rest.length && capability.cli.vnext.arguments !== "semantic") {
|
|
328
|
+
return {
|
|
329
|
+
kind: "error",
|
|
330
|
+
command: command.join(" "),
|
|
331
|
+
error: new VnextUsageError(`Usage: ${capability.cli.vnext.human}`),
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return { kind: "command", command: command.join(" "), capability, argv: rest };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const suggestion = suggestedCommand(catalog, argv[0]);
|
|
338
|
+
return {
|
|
339
|
+
kind: "error",
|
|
340
|
+
command: argv[0] ?? "help",
|
|
341
|
+
error: new VnextUsageError(
|
|
342
|
+
suggestion
|
|
343
|
+
? `Unknown command: ${argv[0]}. Did you mean \`ornn ${suggestion} --help\`?`
|
|
344
|
+
: `Unknown command: ${argv.join(" ")}. Run \`ornn help\` to list registered commands.`,
|
|
345
|
+
{ code: "unknown_command" }
|
|
346
|
+
),
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function commandData(capability) {
|
|
351
|
+
const help = capability.cli.vnext;
|
|
352
|
+
return {
|
|
353
|
+
command: vnextCommand(capability).join(" "),
|
|
354
|
+
outcome: help.outcome,
|
|
355
|
+
human: help.human,
|
|
356
|
+
automation: help.automation,
|
|
357
|
+
accepted_references: help.acceptedReferences ?? [],
|
|
358
|
+
review: help.review ?? null,
|
|
359
|
+
resumable_states: help.resumableStates ?? [],
|
|
360
|
+
related: help.related ?? [],
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function rootData(catalog) {
|
|
365
|
+
const commands = customerCommands(catalog);
|
|
366
|
+
return {
|
|
367
|
+
journey: commands
|
|
368
|
+
.filter((capability) => capability.cli.vnext.category === "journey")
|
|
369
|
+
.map(commandData),
|
|
370
|
+
session: commands
|
|
371
|
+
.filter((capability) => capability.cli.vnext.category === "session")
|
|
372
|
+
.map(commandData),
|
|
373
|
+
platform: commands
|
|
374
|
+
.filter((capability) => capability.cli.vnext.category === "platform")
|
|
375
|
+
.map(commandData),
|
|
376
|
+
advanced_resources: [
|
|
377
|
+
...new Set(
|
|
378
|
+
commands
|
|
379
|
+
.filter((capability) => capability.cli.vnext.category === "resource")
|
|
380
|
+
.map((capability) => vnextCommand(capability)[0])
|
|
381
|
+
),
|
|
382
|
+
],
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function completionData(catalog, shell) {
|
|
387
|
+
const paths = customerCommands(catalog).map((capability) => vnextCommand(capability).join(" "));
|
|
388
|
+
if (shell === "fish") {
|
|
389
|
+
const children = {};
|
|
390
|
+
const lines = ["complete -c ornn -f"];
|
|
391
|
+
for (const path of paths) {
|
|
392
|
+
const parts = path.split(" ");
|
|
393
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
394
|
+
const parent = parts.slice(0, index).join(" ");
|
|
395
|
+
(children[parent] ??= []).push(parts[index]);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
for (const [parent, candidates] of Object.entries(children)) {
|
|
399
|
+
const unique = [...new Set(candidates)].sort().join(" ");
|
|
400
|
+
const condition = parent
|
|
401
|
+
? ` -n '__fish_seen_subcommand_from ${parent.split(" ").join(" ")}'`
|
|
402
|
+
: " -n '__fish_use_subcommand'";
|
|
403
|
+
lines.push(`complete -c ornn -f${condition} -a '${unique}'`);
|
|
404
|
+
}
|
|
405
|
+
return { commands: paths, script: `${lines.join("\n")}\n`, shell };
|
|
406
|
+
}
|
|
407
|
+
const quotedPaths = paths.map((path) => `'${path}'`).join(" ");
|
|
408
|
+
if (shell === "zsh") {
|
|
409
|
+
return {
|
|
410
|
+
commands: paths,
|
|
411
|
+
shell,
|
|
412
|
+
script: `#compdef ornn\n_ornn() {\n local -a paths candidates\n paths=(${quotedPaths})\n local prefix=\"\${(j: :)words[2,CURRENT-1]}\" path rest\n for path in \"\${paths[@]}\"; do\n if [[ -z \"$prefix\" ]]; then rest=\"$path\"\n elif [[ \"$path\" == \"$prefix \"* ]]; then rest=\"\${path#$prefix }\"\n else continue\n fi\n candidates+=(\"\${rest%% *}\")\n done\n compadd -a candidates\n}\ncompdef _ornn ornn\n`,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
return {
|
|
416
|
+
commands: paths,
|
|
417
|
+
shell,
|
|
418
|
+
script: `_ornn_complete() {\n local current=\"\${COMP_WORDS[COMP_CWORD]}\" prefix=\"\" path rest candidate candidates=\"\"\n local paths=(${quotedPaths})\n if (( COMP_CWORD > 1 )); then prefix=\"\${COMP_WORDS[*]:1:COMP_CWORD-1}\"; fi\n for path in \"\${paths[@]}\"; do\n if [[ -z \"$prefix\" ]]; then rest=\"$path\"\n elif [[ \"$path\" == \"$prefix \"* ]]; then rest=\"\${path#\"$prefix \"}\"\n else continue\n fi\n candidate=\"\${rest%% *}\"\n [[ \" $candidates \" == *\" $candidate \"* ]] || candidates+=\" $candidate\"\n done\n COMPREPLY=( $(compgen -W \"$candidates\" -- \"$current\") )\n}\ncomplete -F _ornn_complete ornn\n`,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function groupData(path, capabilities) {
|
|
423
|
+
return {
|
|
424
|
+
path: path.join(" "),
|
|
425
|
+
commands: capabilities.map(commandData),
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function resultEnvelope(
|
|
430
|
+
command,
|
|
431
|
+
data,
|
|
432
|
+
outcome = { class: "ready", phase: "complete" },
|
|
433
|
+
page = null
|
|
434
|
+
) {
|
|
435
|
+
return {
|
|
436
|
+
schema: MACHINE_SCHEMA,
|
|
437
|
+
type: "result",
|
|
438
|
+
command,
|
|
439
|
+
outcome,
|
|
440
|
+
data,
|
|
441
|
+
resume: null,
|
|
442
|
+
action: null,
|
|
443
|
+
page,
|
|
444
|
+
errors: [],
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
function executionEnvelope(command, execution) {
|
|
448
|
+
return {
|
|
449
|
+
...resultEnvelope(command, execution.data, execution.outcome, execution.page ?? null),
|
|
450
|
+
action: execution.action ?? null,
|
|
451
|
+
errors: execution.errors ?? [],
|
|
452
|
+
resume: execution.resume ?? null,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function errorDetail(error) {
|
|
457
|
+
const payload = error?.detail;
|
|
458
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return null;
|
|
459
|
+
if (payload.detail && typeof payload.detail === "object" && !Array.isArray(payload.detail)) {
|
|
460
|
+
return payload.detail;
|
|
461
|
+
}
|
|
462
|
+
if (payload.error && typeof payload.error === "object" && !Array.isArray(payload.error)) {
|
|
463
|
+
return payload.error;
|
|
464
|
+
}
|
|
465
|
+
return payload;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function modeledError(error) {
|
|
469
|
+
if (error instanceof SessionCommandError) {
|
|
470
|
+
return {
|
|
471
|
+
action: error.action,
|
|
472
|
+
exitCode: error.exitCode,
|
|
473
|
+
outcomeClass: error.outcomeClass,
|
|
474
|
+
error: {
|
|
475
|
+
code: error.code,
|
|
476
|
+
message: error.message,
|
|
477
|
+
retryable: error.retryable,
|
|
478
|
+
},
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
if (error instanceof StorageCommandError) {
|
|
482
|
+
return {
|
|
483
|
+
action: error.action ?? null,
|
|
484
|
+
exitCode: error.exitCode,
|
|
485
|
+
outcomeClass: error.outcomeClass,
|
|
486
|
+
error: {
|
|
487
|
+
code: error.code,
|
|
488
|
+
message: error.message,
|
|
489
|
+
retryable: error.retryable,
|
|
490
|
+
...(error.field ? { field: error.field } : {}),
|
|
491
|
+
},
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (
|
|
496
|
+
error instanceof VnextUsageError ||
|
|
497
|
+
error instanceof AdminUsageError ||
|
|
498
|
+
error instanceof VnextContractError ||
|
|
499
|
+
error instanceof AdminMutationError ||
|
|
500
|
+
error instanceof InteractionError
|
|
501
|
+
) {
|
|
502
|
+
return {
|
|
503
|
+
action: error.action ?? null,
|
|
504
|
+
exitCode:
|
|
505
|
+
error instanceof VnextContractError
|
|
506
|
+
? CLI_EXIT_STATUS.failure
|
|
507
|
+
: error instanceof InteractionError
|
|
508
|
+
? error.exitCode
|
|
509
|
+
: CLI_EXIT_STATUS.usage,
|
|
510
|
+
outcomeClass: error.outcomeClass,
|
|
511
|
+
error: {
|
|
512
|
+
code: error.code,
|
|
513
|
+
message: error.message,
|
|
514
|
+
retryable: error.retryable ?? false,
|
|
515
|
+
...(error.field ? { field: error.field } : {}),
|
|
516
|
+
},
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const detail = errorDetail(error);
|
|
521
|
+
const suppliedCode = detail?.code;
|
|
522
|
+
if (typeof suppliedCode === "string" && /^[a-z][a-z0-9_]*$/.test(suppliedCode)) {
|
|
523
|
+
const retryable = detail.retryable === true;
|
|
524
|
+
return {
|
|
525
|
+
exitCode:
|
|
526
|
+
error?.status === 401 || error?.status === 403
|
|
527
|
+
? CLI_EXIT_STATUS.authorization
|
|
528
|
+
: retryable
|
|
529
|
+
? CLI_EXIT_STATUS.unavailable
|
|
530
|
+
: CLI_EXIT_STATUS.failure,
|
|
531
|
+
error: { code: suppliedCode, message: error.message, retryable },
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
if (error instanceof CliApiError && (error.status === 401 || error.status === 403)) {
|
|
535
|
+
return {
|
|
536
|
+
exitCode: CLI_EXIT_STATUS.authorization,
|
|
537
|
+
error: {
|
|
538
|
+
code: error.status === 401 ? "authentication_required" : "authorization_denied",
|
|
539
|
+
message: error.message,
|
|
540
|
+
retryable: false,
|
|
541
|
+
},
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
if (error instanceof CliApiError && (error.status === undefined || error.status >= 500)) {
|
|
545
|
+
return {
|
|
546
|
+
exitCode: CLI_EXIT_STATUS.unavailable,
|
|
547
|
+
error: { code: "service_unavailable", message: error.message, retryable: true },
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
return {
|
|
551
|
+
exitCode: CLI_EXIT_STATUS.failure,
|
|
552
|
+
error: {
|
|
553
|
+
code: "command_failed",
|
|
554
|
+
message: error instanceof Error ? error.message : String(error),
|
|
555
|
+
retryable: false,
|
|
556
|
+
},
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function errorEnvelope(command, modeled) {
|
|
561
|
+
return {
|
|
562
|
+
...resultEnvelope(command, null, {
|
|
563
|
+
class: modeled.outcomeClass ?? "failed",
|
|
564
|
+
phase: modeled.error.code,
|
|
565
|
+
}),
|
|
566
|
+
action: modeled.action ?? null,
|
|
567
|
+
errors: [modeled.error],
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function writeMachine(stdout, format, envelope) {
|
|
572
|
+
const serialized = JSON.stringify(envelope);
|
|
573
|
+
stdout.write(format === "json" ? `${JSON.stringify(envelope, null, 2)}\n` : `${serialized}\n`);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function writeExecutionMachine(stdout, format, command, execution) {
|
|
577
|
+
if (format === "ndjson") {
|
|
578
|
+
for (const progress of execution.progress ?? []) {
|
|
579
|
+
writeMachine(stdout, format, {
|
|
580
|
+
...resultEnvelope(command, progress.data ?? null, progress.outcome),
|
|
581
|
+
type: "progress",
|
|
582
|
+
...(progress.sequence !== undefined ? { sequence: progress.sequence } : {}),
|
|
583
|
+
...(progress.observedAt !== undefined ? { observed_at: progress.observedAt } : {}),
|
|
584
|
+
...(progress.message !== undefined ? { message: progress.message } : {}),
|
|
585
|
+
action: progress.action ?? null,
|
|
586
|
+
errors: progress.errors ?? [],
|
|
587
|
+
resume: progress.resume ?? null,
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
writeMachine(stdout, format, executionEnvelope(command, execution));
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function writeRootHuman(stdout, catalog) {
|
|
595
|
+
const data = rootData(catalog);
|
|
596
|
+
const writeCommands = (title, commands) => {
|
|
597
|
+
stdout.write(`${title}:\n`);
|
|
598
|
+
for (const command of commands) {
|
|
599
|
+
stdout.write(` ${command.command} ${command.outcome}\n`);
|
|
600
|
+
}
|
|
601
|
+
stdout.write("\n");
|
|
602
|
+
};
|
|
603
|
+
stdout.write("Ornn CLI\n\n");
|
|
604
|
+
stdout.write("Usage:\n ornn <command> [options]\n\n");
|
|
605
|
+
stdout.write("Available commands:\n\n");
|
|
606
|
+
writeCommands("Journey", data.journey);
|
|
607
|
+
writeCommands("Session", data.session);
|
|
608
|
+
writeCommands("Platform", data.platform);
|
|
609
|
+
stdout.write("Advanced resources:\n");
|
|
610
|
+
for (const resource of data.advanced_resources) stdout.write(` ${resource}\n`);
|
|
611
|
+
stdout.write(
|
|
612
|
+
"\nRun `ornn resources` for advanced outcomes or `ornn help <command>` for scoped help.\n"
|
|
613
|
+
);
|
|
614
|
+
stdout.write("Redirected commands require --format human, json, or ndjson.\n");
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function writeHelpHuman(stdout, capability) {
|
|
618
|
+
const data = commandData(capability);
|
|
619
|
+
stdout.write(`${data.command}\n\nOutcome: ${data.outcome}\n\n`);
|
|
620
|
+
stdout.write(`Usage:\n ${data.human}\n\nAutomation:\n ${data.automation}\n`);
|
|
621
|
+
stdout.write(
|
|
622
|
+
`\nAccepted references: ${data.accepted_references.length ? data.accepted_references.join(", ") : "none"}\n`
|
|
623
|
+
);
|
|
624
|
+
stdout.write(`Review: ${data.review ?? "none"}\n`);
|
|
625
|
+
stdout.write(
|
|
626
|
+
`Resumable states: ${data.resumable_states.length ? data.resumable_states.join(", ") : "none"}\n`
|
|
627
|
+
);
|
|
628
|
+
stdout.write(
|
|
629
|
+
`Related: ${data.related.length ? data.related.map((command) => `ornn ${command}`).join(", ") : "none"}\n`
|
|
630
|
+
);
|
|
631
|
+
stdout.write("Environment: redirected vNext commands require --format human, json, or ndjson.\n");
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
function writeGroupHelpHuman(stdout, path, capabilities) {
|
|
635
|
+
stdout.write(`${path.join(" ")}\n\nAvailable commands:\n`);
|
|
636
|
+
for (const capability of capabilities) {
|
|
637
|
+
const data = commandData(capability);
|
|
638
|
+
stdout.write(` ${data.human}\n ${data.outcome}\n`);
|
|
639
|
+
}
|
|
640
|
+
stdout.write("\nRun `ornn help <full-command>` for references, review, and recovery details.\n");
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function writeStatusHuman(stdout, snapshot) {
|
|
644
|
+
stdout.write(`${snapshot?.overallStatusLabel ?? snapshot?.status ?? "Status available"}\n`);
|
|
645
|
+
if (snapshot?.overallDetail) stdout.write(`${snapshot.overallDetail}\n`);
|
|
646
|
+
if (snapshot?.generatedAtLabel) stdout.write(`Generated: ${snapshot.generatedAtLabel}\n`);
|
|
647
|
+
for (const group of snapshot?.groups ?? []) {
|
|
648
|
+
stdout.write(`${group.name}: ${group.statusLabel}\n`);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function writeContextHuman(stdout, data) {
|
|
653
|
+
const user = data.user?.email ?? data.user?.id ?? "unknown";
|
|
654
|
+
stdout.write(`User: ${user}\n`);
|
|
655
|
+
if (data.selected_organization) {
|
|
656
|
+
stdout.write(`Organization: ${data.selected_organization.name}\n`);
|
|
657
|
+
stdout.write(`Approved: ${data.selected_organization.approved ? "yes" : "no"}\n`);
|
|
658
|
+
} else {
|
|
659
|
+
stdout.write("Organization: none selected\n");
|
|
660
|
+
stdout.write("Approved: not selected\n");
|
|
661
|
+
}
|
|
662
|
+
stdout.write("Available organizations:\n");
|
|
663
|
+
if (!data.organizations?.length) {
|
|
664
|
+
stdout.write(" none\n");
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
for (const organization of data.organizations) {
|
|
668
|
+
stdout.write(
|
|
669
|
+
` ${organization.name} (${organization.approved ? "approved" : "not approved"})\n`
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function writeCommandHuman(stdout, stderr, capability, execution) {
|
|
675
|
+
const { action, data, errors = [] } = execution;
|
|
676
|
+
if (capability.id === "identity.status") {
|
|
677
|
+
writeStatusHuman(stdout, data);
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
if (capability.id === "identity.login") {
|
|
681
|
+
if (data.session_saved) stdout.write("Logged in to Ornn.\n");
|
|
682
|
+
if (data.user) writeContextHuman(stdout, data);
|
|
683
|
+
if (action?.type === "browser") {
|
|
684
|
+
stderr.write(`Complete login in a browser:\n${action.url}\nCode: ${action.code}\n`);
|
|
685
|
+
} else if (action?.command) {
|
|
686
|
+
stderr.write(`Next: ${action.command}\n`);
|
|
687
|
+
}
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
if (capability.id === "identity.context") {
|
|
691
|
+
writeContextHuman(stdout, data);
|
|
692
|
+
if (action?.command) stderr.write(`Next: ${action.command}\n`);
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
if (capability.id === "identity.logout") {
|
|
696
|
+
stdout.write(
|
|
697
|
+
data.remote_session_revoked ? "Logged out of Ornn.\n" : "Local Ornn session cleared.\n"
|
|
698
|
+
);
|
|
699
|
+
for (const error of errors) stderr.write(`${error.message}\n`);
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
if (capability.id === "cli.version") {
|
|
703
|
+
stdout.write(`Ornn CLI ${data.version}\n`);
|
|
704
|
+
stdout.write(`Machine schema: ${data.machine_schema}\n`);
|
|
705
|
+
stdout.write(`Update channel: ${data.update_channel}\n`);
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
if (capability.id === "cli.resources") {
|
|
709
|
+
stdout.write("Advanced resources\n");
|
|
710
|
+
if (!data.resources.length) {
|
|
711
|
+
stdout.write("No advanced resource commands are registered yet.\n");
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
for (const resource of data.resources) {
|
|
715
|
+
stdout.write(`${resource.command}: ${resource.outcome}\n`);
|
|
716
|
+
stdout.write(` Human: ${resource.human}\n`);
|
|
717
|
+
stdout.write(` Automation: ${resource.automation}\n`);
|
|
718
|
+
}
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
if (capability.id === "cli.completion") {
|
|
722
|
+
stdout.write(data.script);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
if (capability.id.startsWith("storage.volumes.")) {
|
|
726
|
+
writeStorageResultHuman(stdout, capability.id, execution);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (capability.id.startsWith("storage.external-buckets.")) {
|
|
730
|
+
writeExternalBucketResultHuman(stdout, stderr, capability.id, execution);
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
if (execution.presentation?.lines) {
|
|
734
|
+
for (const line of execution.presentation.lines) stdout.write(`${line}\n`);
|
|
735
|
+
if (execution.presentation.next) stderr.write(`Next: ${execution.presentation.next}\n`);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (action?.url) {
|
|
739
|
+
stderr.write(`${action.description ?? "External action required"}\n${action.url}\n`);
|
|
740
|
+
const command =
|
|
741
|
+
action.command ??
|
|
742
|
+
(execution.resume?.ref
|
|
743
|
+
? `ornn ${vnextCommand(capability).join(" ")} --resume ${execution.resume.ref}`
|
|
744
|
+
: null);
|
|
745
|
+
if (command) stderr.write(`Next: ${command}\n`);
|
|
746
|
+
} else if (data !== null && data !== undefined) {
|
|
747
|
+
stdout.write(`${JSON.stringify(data, null, 2)}\n`);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const CUSTOMER_FORBIDDEN_FIELDS = {
|
|
752
|
+
client_secret: true,
|
|
753
|
+
clientSecret: true,
|
|
754
|
+
payment_secret: true,
|
|
755
|
+
paymentSecret: true,
|
|
756
|
+
operator: true,
|
|
757
|
+
operatorId: true,
|
|
758
|
+
operatorName: true,
|
|
759
|
+
operator_id: true,
|
|
760
|
+
operator_name: true,
|
|
761
|
+
internal_owner: true,
|
|
762
|
+
internalOwner: true,
|
|
763
|
+
payment_intent_client_secret: true,
|
|
764
|
+
checkout_key: true,
|
|
765
|
+
checkoutKey: true,
|
|
766
|
+
provider_id: true,
|
|
767
|
+
provider_name: true,
|
|
768
|
+
providerId: true,
|
|
769
|
+
providerName: true,
|
|
770
|
+
paymentIntentClientSecret: true,
|
|
771
|
+
payment_provider: true,
|
|
772
|
+
paymentProvider: true,
|
|
773
|
+
provider: true,
|
|
774
|
+
site_operator: true,
|
|
775
|
+
siteOperator: true,
|
|
776
|
+
site_operator_id: true,
|
|
777
|
+
site_operator_name: true,
|
|
778
|
+
siteOperatorId: true,
|
|
779
|
+
siteOperatorName: true,
|
|
780
|
+
reconciliation: true,
|
|
781
|
+
reconciliation_status: true,
|
|
782
|
+
reconciliationStatus: true,
|
|
783
|
+
stripe_client_secret: true,
|
|
784
|
+
stripeClientSecret: true,
|
|
785
|
+
stripe_invoice_id: true,
|
|
786
|
+
stripeInvoiceId: true,
|
|
787
|
+
supplier: true,
|
|
788
|
+
supplier_id: true,
|
|
789
|
+
supplier_name: true,
|
|
790
|
+
supplierId: true,
|
|
791
|
+
supplierName: true,
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
function assertCustomerResultSafe(value) {
|
|
795
|
+
if (Array.isArray(value)) {
|
|
796
|
+
for (const item of value) assertCustomerResultSafe(item);
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
if (!value || typeof value !== "object") return;
|
|
800
|
+
for (const [key, child] of Object.entries(value)) {
|
|
801
|
+
if (CUSTOMER_FORBIDDEN_FIELDS[key] === true) {
|
|
802
|
+
throw new VnextContractError(
|
|
803
|
+
"The gateway returned an unexpected response shape.",
|
|
804
|
+
"customer_result_unsafe"
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
assertCustomerResultSafe(child);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function parseCapabilityInput(capability, rawInput) {
|
|
812
|
+
const parsed = capability.input.safeParse(rawInput);
|
|
813
|
+
if (parsed.success) return parsed.data;
|
|
814
|
+
const issue = parsed.error.issues[0];
|
|
815
|
+
if (issue?.path?.[0] === "$positionals") {
|
|
816
|
+
const usage = capability.cli?.vnext?.human ?? `ornn ${vnextCommand(capability).join(" ")}`;
|
|
817
|
+
throw new VnextUsageError(`Usage: ${usage}`);
|
|
818
|
+
}
|
|
819
|
+
const field = issue?.path?.length ? issue.path.join(".") : undefined;
|
|
820
|
+
throw new InteractionError(issue?.message ?? "Invalid command input.", {
|
|
821
|
+
code: "invalid_input",
|
|
822
|
+
exitCode: CLI_EXIT_STATUS.usage,
|
|
823
|
+
field,
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
async function customerCapabilityFetch(io, path, init = {}, sessionSnapshot) {
|
|
828
|
+
const authRequired = path !== "/v1/cli/status";
|
|
829
|
+
const session = sessionSnapshot?.current;
|
|
830
|
+
const method = init.method ?? "GET";
|
|
831
|
+
if (authRequired && sessionSnapshot && !session?.accessToken) {
|
|
832
|
+
throw new CliApiError("Not logged in. Run `ornn login` first.", { status: 401 });
|
|
833
|
+
}
|
|
834
|
+
if (authRequired && sessionSnapshot && !["GET", "HEAD"].includes(method)) {
|
|
835
|
+
const current = await loadAuthSession({ env: io.env });
|
|
836
|
+
if (current?.accessToken !== session?.accessToken) {
|
|
837
|
+
throw new InteractionError(
|
|
838
|
+
"Authentication changed while this command was running; no mutation was sent. Review and retry.",
|
|
839
|
+
{
|
|
840
|
+
code: "auth_session_changed",
|
|
841
|
+
exitCode: 3,
|
|
842
|
+
}
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
return cliRequest({
|
|
847
|
+
authRequired,
|
|
848
|
+
endpoint: path,
|
|
849
|
+
env: io.env,
|
|
850
|
+
fetchImpl: io.fetchImpl,
|
|
851
|
+
headers: init.headers,
|
|
852
|
+
method,
|
|
853
|
+
...(authRequired && session ? { session } : {}),
|
|
854
|
+
...(init.body !== undefined ? { body: init.body } : {}),
|
|
855
|
+
...(init.signal !== undefined ? { signal: init.signal } : {}),
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
async function executeCapability(
|
|
860
|
+
capability,
|
|
861
|
+
catalog,
|
|
862
|
+
io,
|
|
863
|
+
version,
|
|
864
|
+
{
|
|
865
|
+
audience = "customer",
|
|
866
|
+
input,
|
|
867
|
+
role = "user",
|
|
868
|
+
confirmed = false,
|
|
869
|
+
argv = [],
|
|
870
|
+
executionRuntime,
|
|
871
|
+
} = {}
|
|
872
|
+
) {
|
|
873
|
+
const sessionCommand = SESSION_CAPABILITY_IDS.has(capability.id);
|
|
874
|
+
const authSnapshot = executionRuntime?.authSnapshot;
|
|
875
|
+
if (
|
|
876
|
+
audience === "customer" &&
|
|
877
|
+
!sessionCommand &&
|
|
878
|
+
authSnapshot &&
|
|
879
|
+
authSnapshot.current === undefined
|
|
880
|
+
) {
|
|
881
|
+
authSnapshot.current = await loadAuthSession({ env: io.env });
|
|
882
|
+
}
|
|
883
|
+
const rawInput =
|
|
884
|
+
input ??
|
|
885
|
+
(sessionCommand ? parseSessionCommandInput(capability.id, argv) : parseSemanticArguments(argv));
|
|
886
|
+
const interaction =
|
|
887
|
+
audience === "customer" && !sessionCommand
|
|
888
|
+
? createInteractionRuntime({
|
|
889
|
+
eligibleHuman: executionRuntime.eligibleHuman,
|
|
890
|
+
extensions: {
|
|
891
|
+
...executionRuntime.interactionExtensions,
|
|
892
|
+
externalBuckets(command, externalBucketInput) {
|
|
893
|
+
return executeExternalBucketCommand(command, externalBucketInput, {
|
|
894
|
+
fetch: (path, init) => customerCapabilityFetch(io, path, init, authSnapshot),
|
|
895
|
+
interaction: this,
|
|
896
|
+
signal: io.signal,
|
|
897
|
+
sleep: io.sleep,
|
|
898
|
+
stderr: io.stderr,
|
|
899
|
+
});
|
|
900
|
+
},
|
|
901
|
+
evidence(command, evidenceInput) {
|
|
902
|
+
return executeEvidenceCommand(command, evidenceInput, {
|
|
903
|
+
fetch: (path, init) => customerCapabilityFetch(io, path, init, authSnapshot),
|
|
904
|
+
interaction: this,
|
|
905
|
+
now: io.now,
|
|
906
|
+
signal: io.signal,
|
|
907
|
+
sleep:
|
|
908
|
+
io.sleep ??
|
|
909
|
+
((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds))),
|
|
910
|
+
stdout: io.stdout ?? process.stdout,
|
|
911
|
+
});
|
|
912
|
+
},
|
|
913
|
+
now: io.now,
|
|
914
|
+
signal: io.signal,
|
|
915
|
+
sleep: io.sleep,
|
|
916
|
+
stderr: io.stderr ?? process.stderr,
|
|
917
|
+
cluster(command, clusterInput) {
|
|
918
|
+
return executeKubernetesClusterCommand(command, clusterInput, {
|
|
919
|
+
fetch: (path, init) => customerCapabilityFetch(io, path, init, authSnapshot),
|
|
920
|
+
interaction: this,
|
|
921
|
+
now: io.now,
|
|
922
|
+
signal: io.signal,
|
|
923
|
+
sleep: io.sleep,
|
|
924
|
+
stderr: io.stderr ?? process.stderr,
|
|
925
|
+
});
|
|
926
|
+
},
|
|
927
|
+
mounts(command, mountInput) {
|
|
928
|
+
return executeMountCommand(command, mountInput, {
|
|
929
|
+
fetch: (path, init) => customerCapabilityFetch(io, path, init, authSnapshot),
|
|
930
|
+
interaction: this,
|
|
931
|
+
});
|
|
932
|
+
},
|
|
933
|
+
storage(command, storageInput) {
|
|
934
|
+
return executeStorageCommand(command, storageInput, {
|
|
935
|
+
env: io.env,
|
|
936
|
+
fetch: (path, init) => customerCapabilityFetch(io, path, init, authSnapshot),
|
|
937
|
+
fetchImpl: io.fetchImpl,
|
|
938
|
+
interaction: this,
|
|
939
|
+
});
|
|
940
|
+
},
|
|
941
|
+
},
|
|
942
|
+
format: executionRuntime.format,
|
|
943
|
+
formatProvided: executionRuntime.formatProvided,
|
|
944
|
+
io,
|
|
945
|
+
options: rawInput,
|
|
946
|
+
})
|
|
947
|
+
: null;
|
|
948
|
+
const installerBaseUrl =
|
|
949
|
+
audience === "admin"
|
|
950
|
+
? (resolveInstallerBaseUrl({ env: io.env }) ??
|
|
951
|
+
`${resolveApiBaseUrl({ env: io.env })}/v1/orchestrator`)
|
|
952
|
+
: undefined;
|
|
953
|
+
const parsedInput = parseCapabilityInput(capability, rawInput);
|
|
954
|
+
let data;
|
|
955
|
+
try {
|
|
956
|
+
data = await capability.execute(
|
|
957
|
+
{
|
|
958
|
+
actor: "",
|
|
959
|
+
confirmed,
|
|
960
|
+
installerBaseUrl,
|
|
961
|
+
role,
|
|
962
|
+
fetch: (path, init = {}) => {
|
|
963
|
+
if (executionRuntime?.transport) return executionRuntime.transport(path, init);
|
|
964
|
+
if (
|
|
965
|
+
audience === "admin" &&
|
|
966
|
+
capability.id === "listings.create" &&
|
|
967
|
+
path === HARDWARE_CATALOG_PATH
|
|
968
|
+
) {
|
|
969
|
+
return operatorRequest({
|
|
970
|
+
endpoint: "/catalog/hardware",
|
|
971
|
+
env: io.env,
|
|
972
|
+
fetchImpl: io.fetchImpl,
|
|
973
|
+
method: init.method ?? "GET",
|
|
974
|
+
...(init.headers !== undefined ? { headers: init.headers } : {}),
|
|
975
|
+
...(init.signal !== undefined ? { signal: init.signal } : {}),
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
if (audience === "admin") {
|
|
979
|
+
const search = new URL(path, "https://cli.invalid").search;
|
|
980
|
+
const endpoint =
|
|
981
|
+
capability.id === "telemetry.latest"
|
|
982
|
+
? `/telemetry/latest${search}`
|
|
983
|
+
: capability.id === "logs.latest"
|
|
984
|
+
? `/logs/latest${search}`
|
|
985
|
+
: path;
|
|
986
|
+
return operatorRequest({
|
|
987
|
+
endpoint,
|
|
988
|
+
env: io.env,
|
|
989
|
+
fetchImpl: io.fetchImpl,
|
|
990
|
+
method: init.method ?? "GET",
|
|
991
|
+
...(init.body !== undefined ? { body: init.body } : {}),
|
|
992
|
+
...(init.headers !== undefined ? { headers: init.headers } : {}),
|
|
993
|
+
...((init.signal ?? io.signal) !== undefined
|
|
994
|
+
? { signal: init.signal ?? io.signal }
|
|
995
|
+
: {}),
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
return customerCapabilityFetch(io, path, init, authSnapshot);
|
|
999
|
+
},
|
|
1000
|
+
cli: {
|
|
1001
|
+
catalog,
|
|
1002
|
+
interaction: interaction ?? undefined,
|
|
1003
|
+
completion: async ({ shell }) => completionData(catalog, shell),
|
|
1004
|
+
machineSchema: MACHINE_SCHEMA,
|
|
1005
|
+
session: (command, sessionInput) =>
|
|
1006
|
+
executeSessionCommand(command, sessionInput, {
|
|
1007
|
+
...executionRuntime,
|
|
1008
|
+
sessionSnapshot: authSnapshot,
|
|
1009
|
+
}),
|
|
1010
|
+
updateChannel: UPDATE_CHANNEL,
|
|
1011
|
+
version,
|
|
1012
|
+
},
|
|
1013
|
+
},
|
|
1014
|
+
parsedInput
|
|
1015
|
+
);
|
|
1016
|
+
} finally {
|
|
1017
|
+
interaction?.close();
|
|
1018
|
+
}
|
|
1019
|
+
if (audience === "customer") assertCustomerResultSafe(data);
|
|
1020
|
+
if (audience === "admin" || sessionCommand || isInteractionExecution(data)) return data;
|
|
1021
|
+
return {
|
|
1022
|
+
action: null,
|
|
1023
|
+
data,
|
|
1024
|
+
errors: [],
|
|
1025
|
+
exitCode: CLI_EXIT_STATUS.success,
|
|
1026
|
+
outcome: { class: "ready", phase: "complete" },
|
|
1027
|
+
page: null,
|
|
1028
|
+
presentation: null,
|
|
1029
|
+
resume: null,
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
async function resolveAdminRole(io) {
|
|
1034
|
+
await cliRequest({
|
|
1035
|
+
endpoint: "/v1/staff/context",
|
|
1036
|
+
env: io.env,
|
|
1037
|
+
fetchImpl: io.fetchImpl,
|
|
1038
|
+
method: "HEAD",
|
|
1039
|
+
raw: true,
|
|
1040
|
+
});
|
|
1041
|
+
return "admin";
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
export async function runCatalogCli(argv, io, { version }) {
|
|
1045
|
+
const capabilities = await loadCapabilities();
|
|
1046
|
+
const catalog = capabilities.catalog;
|
|
1047
|
+
const parsed = extractFormat(argv);
|
|
1048
|
+
const request = resolveRequest(
|
|
1049
|
+
catalog,
|
|
1050
|
+
parsed.remaining,
|
|
1051
|
+
capabilities.findLegacyCliMigration,
|
|
1052
|
+
capabilities.CLI_MIGRATION_GUIDE_URL
|
|
1053
|
+
);
|
|
1054
|
+
|
|
1055
|
+
const stdin = io.stdin ?? process.stdin;
|
|
1056
|
+
const stdout = io.stdout ?? process.stdout;
|
|
1057
|
+
const stderr = io.stderr ?? process.stderr;
|
|
1058
|
+
const format = parsed.format ?? "human";
|
|
1059
|
+
const machine = format === "json" || format === "ndjson";
|
|
1060
|
+
const eligibleHuman = !machine && stdin.isTTY === true && stdout.isTTY === true;
|
|
1061
|
+
if (parsed.error) {
|
|
1062
|
+
const modeled = modeledError(parsed.error);
|
|
1063
|
+
if (machine) {
|
|
1064
|
+
writeMachine(stdout, format, errorEnvelope(request.command, modeled));
|
|
1065
|
+
} else {
|
|
1066
|
+
stderr.write(`${modeled.error.message}\n`);
|
|
1067
|
+
}
|
|
1068
|
+
return {
|
|
1069
|
+
exitCode: modeled.exitCode,
|
|
1070
|
+
suppressUpdateNotification: true,
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
const customerRawPrint =
|
|
1074
|
+
request.kind === "command" &&
|
|
1075
|
+
request.capability.id === "vpn.connections.configure" &&
|
|
1076
|
+
parsed.remaining.includes("--print");
|
|
1077
|
+
const rawPrint =
|
|
1078
|
+
(request.kind === "admin" && parsed.remaining.includes("--print")) || customerRawPrint;
|
|
1079
|
+
const unformattedUpdate = request.kind === "command" && request.capability.id === "cli.update";
|
|
1080
|
+
if (
|
|
1081
|
+
parsed.format === undefined &&
|
|
1082
|
+
!eligibleHuman &&
|
|
1083
|
+
!rawPrint &&
|
|
1084
|
+
!unformattedUpdate &&
|
|
1085
|
+
request.kind !== "error"
|
|
1086
|
+
) {
|
|
1087
|
+
stderr.write(
|
|
1088
|
+
"Output format is required outside an interactive terminal. Choose --format human, --format json, or --format ndjson.\n"
|
|
1089
|
+
);
|
|
1090
|
+
return {
|
|
1091
|
+
exitCode: CLI_EXIT_STATUS.usage,
|
|
1092
|
+
suppressUpdateNotification: true,
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
const success = {
|
|
1097
|
+
exitCode: CLI_EXIT_STATUS.success,
|
|
1098
|
+
suppressUpdateNotification: machine,
|
|
1099
|
+
};
|
|
1100
|
+
let activeCommand = request.command;
|
|
1101
|
+
try {
|
|
1102
|
+
if (request.kind === "error") throw request.error;
|
|
1103
|
+
if (request.kind === "root") {
|
|
1104
|
+
const data = rootData(catalog);
|
|
1105
|
+
if (machine) writeMachine(stdout, format, resultEnvelope(request.command, data));
|
|
1106
|
+
else writeRootHuman(stdout, catalog);
|
|
1107
|
+
return success;
|
|
1108
|
+
}
|
|
1109
|
+
if (request.kind === "help") {
|
|
1110
|
+
const data = commandData(request.capability);
|
|
1111
|
+
if (machine) writeMachine(stdout, format, resultEnvelope(request.command, data));
|
|
1112
|
+
else writeHelpHuman(stdout, request.capability);
|
|
1113
|
+
return success;
|
|
1114
|
+
}
|
|
1115
|
+
if (request.kind === "help-group") {
|
|
1116
|
+
const data = groupData(request.path, request.capabilities);
|
|
1117
|
+
if (machine) writeMachine(stdout, format, resultEnvelope(request.command, data));
|
|
1118
|
+
else writeGroupHelpHuman(stdout, request.path, request.capabilities);
|
|
1119
|
+
return success;
|
|
1120
|
+
}
|
|
1121
|
+
if (request.kind === "command" && request.capability.id === "cli.update") {
|
|
1122
|
+
if (machine) {
|
|
1123
|
+
throw new VnextUsageError("ornn update supports human output only. Use --format human.", {
|
|
1124
|
+
field: "format",
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
const input = parseCapabilityInput(
|
|
1128
|
+
request.capability,
|
|
1129
|
+
parseSemanticArguments(request.argv ?? [])
|
|
1130
|
+
);
|
|
1131
|
+
const exitCode = await updateCommand({
|
|
1132
|
+
allowMajor: input.major === true,
|
|
1133
|
+
binPath: io.binPath ?? process.argv[1],
|
|
1134
|
+
currentVersion: version,
|
|
1135
|
+
env: io.env,
|
|
1136
|
+
fetchImpl: io.fetchImpl,
|
|
1137
|
+
platform: io.platform,
|
|
1138
|
+
spawnProcess: io.spawnProcess,
|
|
1139
|
+
stderr,
|
|
1140
|
+
stdout,
|
|
1141
|
+
});
|
|
1142
|
+
return {
|
|
1143
|
+
exitCode,
|
|
1144
|
+
suppressUpdateNotification: true,
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
if (request.kind === "admin") {
|
|
1148
|
+
const earlyCommand = adminCommandName(catalog, request.argv);
|
|
1149
|
+
if (earlyCommand) activeCommand = earlyCommand;
|
|
1150
|
+
preflightAdminCommand(catalog, request.argv, {
|
|
1151
|
+
eligibleHuman,
|
|
1152
|
+
machine,
|
|
1153
|
+
});
|
|
1154
|
+
const role = await resolveAdminRole(io);
|
|
1155
|
+
const adminRequest = resolveAdminRequest(catalog, capabilities.ADMIN_CLI_ROOTS, request.argv);
|
|
1156
|
+
activeCommand = adminRequest.command;
|
|
1157
|
+
if (adminRequest.kind === "root") {
|
|
1158
|
+
const data = adminRootData(catalog, capabilities.ADMIN_CLI_ROOTS, adminRequest.root);
|
|
1159
|
+
if (machine) writeMachine(stdout, format, resultEnvelope(adminRequest.command, data));
|
|
1160
|
+
else writeAdminRootHuman(stdout, data);
|
|
1161
|
+
return success;
|
|
1162
|
+
}
|
|
1163
|
+
if (adminRequest.kind === "help") {
|
|
1164
|
+
const data = adminCommandData(adminRequest.capability);
|
|
1165
|
+
if (machine) writeMachine(stdout, format, resultEnvelope(adminRequest.command, data));
|
|
1166
|
+
else writeAdminHelpHuman(stdout, adminRequest.capability);
|
|
1167
|
+
return success;
|
|
1168
|
+
}
|
|
1169
|
+
const execute = (input, { confirmed = false, transport } = {}) =>
|
|
1170
|
+
executeCapability(adminRequest.capability, catalog, io, version, {
|
|
1171
|
+
audience: "admin",
|
|
1172
|
+
confirmed,
|
|
1173
|
+
executionRuntime: transport ? { transport } : undefined,
|
|
1174
|
+
input,
|
|
1175
|
+
role,
|
|
1176
|
+
});
|
|
1177
|
+
const fetch = (path, init = {}) =>
|
|
1178
|
+
operatorRequest({
|
|
1179
|
+
endpoint: path,
|
|
1180
|
+
env: io.env,
|
|
1181
|
+
fetchImpl: io.fetchImpl,
|
|
1182
|
+
method: init.method ?? "GET",
|
|
1183
|
+
...(init.body !== undefined ? { body: init.body } : {}),
|
|
1184
|
+
...(init.headers !== undefined ? { headers: init.headers } : {}),
|
|
1185
|
+
...(init.signal !== undefined ? { signal: init.signal } : {}),
|
|
1186
|
+
});
|
|
1187
|
+
const execution = adminRequest.handler
|
|
1188
|
+
? await adminRequest.handler.run({
|
|
1189
|
+
capability: adminRequest.capability,
|
|
1190
|
+
execute,
|
|
1191
|
+
fetch,
|
|
1192
|
+
input: adminRequest.input,
|
|
1193
|
+
mutation: adminRequest.mutation,
|
|
1194
|
+
role,
|
|
1195
|
+
runtime: {
|
|
1196
|
+
eligibleHuman,
|
|
1197
|
+
env: io.env,
|
|
1198
|
+
formatProvided: parsed.format !== undefined,
|
|
1199
|
+
machine,
|
|
1200
|
+
now: io.now,
|
|
1201
|
+
signal: io.signal,
|
|
1202
|
+
sleep: io.sleep,
|
|
1203
|
+
stderr,
|
|
1204
|
+
stdin,
|
|
1205
|
+
stdout,
|
|
1206
|
+
},
|
|
1207
|
+
})
|
|
1208
|
+
: {
|
|
1209
|
+
data: await execute(adminRequest.input),
|
|
1210
|
+
exitCode: CLI_EXIT_STATUS.success,
|
|
1211
|
+
outcome: { class: "ready", phase: "complete" },
|
|
1212
|
+
};
|
|
1213
|
+
if (execution.raw || execution.suppressResult) {
|
|
1214
|
+
return {
|
|
1215
|
+
exitCode: execution.exitCode ?? CLI_EXIT_STATUS.success,
|
|
1216
|
+
suppressUpdateNotification: true,
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
const result = finalizeAdminResult(
|
|
1220
|
+
adminRequest.capability,
|
|
1221
|
+
execution.data,
|
|
1222
|
+
execution.pagination ?? adminRequest.pagination
|
|
1223
|
+
);
|
|
1224
|
+
const envelope = {
|
|
1225
|
+
...resultEnvelope(adminRequest.command, result.data, execution.outcome, result.page),
|
|
1226
|
+
action: execution.action ?? null,
|
|
1227
|
+
errors: execution.errors ?? [],
|
|
1228
|
+
resume: execution.resume ?? null,
|
|
1229
|
+
};
|
|
1230
|
+
if (machine) writeMachine(stdout, format, envelope);
|
|
1231
|
+
else writeAdminResultHuman(stdout, adminRequest.command, result);
|
|
1232
|
+
return {
|
|
1233
|
+
exitCode: execution.exitCode ?? CLI_EXIT_STATUS.success,
|
|
1234
|
+
suppressUpdateNotification:
|
|
1235
|
+
machine || (execution.exitCode ?? CLI_EXIT_STATUS.success) !== CLI_EXIT_STATUS.success,
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
const authSnapshot = {};
|
|
1240
|
+
const execution = await executeCapability(request.capability, catalog, io, version, {
|
|
1241
|
+
argv: request.argv ?? [],
|
|
1242
|
+
executionRuntime: {
|
|
1243
|
+
authSnapshot,
|
|
1244
|
+
eligibleHuman,
|
|
1245
|
+
env: io.env,
|
|
1246
|
+
fetchImpl: io.fetchImpl,
|
|
1247
|
+
machine,
|
|
1248
|
+
format,
|
|
1249
|
+
formatProvided: parsed.format !== undefined,
|
|
1250
|
+
interactionExtensions: {
|
|
1251
|
+
exportSecret: (options) => exportVpnSecret(options, { machine, stdout }),
|
|
1252
|
+
prepareSecretExport: prepareVpnSecretExport,
|
|
1253
|
+
wireGuardPublicKeyFromFile,
|
|
1254
|
+
hasLocalNetworkAddress,
|
|
1255
|
+
workspaceResume: {
|
|
1256
|
+
clear: (id) =>
|
|
1257
|
+
clearPendingWorkspace(id, {
|
|
1258
|
+
env: io.env,
|
|
1259
|
+
expectedAccessToken: authSnapshot.current?.accessToken,
|
|
1260
|
+
}),
|
|
1261
|
+
load: () => loadPendingWorkspace({ env: io.env, session: authSnapshot.current }),
|
|
1262
|
+
save: (value) =>
|
|
1263
|
+
savePendingWorkspace(value, {
|
|
1264
|
+
env: io.env,
|
|
1265
|
+
expectedAccessToken: authSnapshot.current?.accessToken,
|
|
1266
|
+
}),
|
|
1267
|
+
},
|
|
1268
|
+
launch: (state) =>
|
|
1269
|
+
dispatchLaunchWithHandlers(state, [
|
|
1270
|
+
bareMetalLaunchModeHandler,
|
|
1271
|
+
vmLaunchModeHandler,
|
|
1272
|
+
kubernetesLaunchModeHandler,
|
|
1273
|
+
slurmLaunchModeHandler,
|
|
1274
|
+
...(io.launchModeHandlers ?? []),
|
|
1275
|
+
]),
|
|
1276
|
+
sshKeys: (command, input, context) =>
|
|
1277
|
+
executeSshKeyCommand(command, input, context, context.cli.interaction),
|
|
1278
|
+
machines: (command, input, context) =>
|
|
1279
|
+
executeMachineCommand(command, input, context, context.cli.interaction),
|
|
1280
|
+
waitForGoal: (options) =>
|
|
1281
|
+
waitForGoal(options, {
|
|
1282
|
+
format,
|
|
1283
|
+
now: io.now,
|
|
1284
|
+
signal: io.signal,
|
|
1285
|
+
sleep: io.sleep,
|
|
1286
|
+
stderr,
|
|
1287
|
+
stdout,
|
|
1288
|
+
}),
|
|
1289
|
+
resolveConnectTarget: (state) =>
|
|
1290
|
+
resolveConnectTargetWithHandlers(
|
|
1291
|
+
state,
|
|
1292
|
+
io.connectModeHandlers ?? [
|
|
1293
|
+
createSlurmConnectModeHandler({ spawnProcess: io.spawnProcess }),
|
|
1294
|
+
createBareMetalConnectModeHandler({ spawnProcess: io.spawnProcess }),
|
|
1295
|
+
createVmConnectModeHandler({ spawnProcess: io.spawnProcess }),
|
|
1296
|
+
kubernetesConnectModeHandler,
|
|
1297
|
+
]
|
|
1298
|
+
),
|
|
1299
|
+
async continueConnect(state) {
|
|
1300
|
+
if (io.connectModeHandlers) {
|
|
1301
|
+
return continueConnectWithHandlers(state, io.connectModeHandlers);
|
|
1302
|
+
}
|
|
1303
|
+
const normalized = {
|
|
1304
|
+
...state,
|
|
1305
|
+
context: state.context ?? {
|
|
1306
|
+
fetch: (path, init) => customerCapabilityFetch(io, path, init, authSnapshot),
|
|
1307
|
+
},
|
|
1308
|
+
input: state.input ?? this.options,
|
|
1309
|
+
interaction: state.interaction ?? this,
|
|
1310
|
+
};
|
|
1311
|
+
return continueConnectWithHandlers(normalized, [
|
|
1312
|
+
createSlurmConnectModeHandler({ spawnProcess: io.spawnProcess }),
|
|
1313
|
+
createBareMetalConnectModeHandler({ spawnProcess: io.spawnProcess }),
|
|
1314
|
+
createVmConnectModeHandler({ spawnProcess: io.spawnProcess }),
|
|
1315
|
+
kubernetesConnectModeHandler,
|
|
1316
|
+
]);
|
|
1317
|
+
},
|
|
1318
|
+
},
|
|
1319
|
+
now: io.now,
|
|
1320
|
+
openBrowserImpl: io.openBrowser,
|
|
1321
|
+
sleep: io.sleep,
|
|
1322
|
+
signal: io.signal,
|
|
1323
|
+
stderr,
|
|
1324
|
+
stdin,
|
|
1325
|
+
stdout,
|
|
1326
|
+
},
|
|
1327
|
+
});
|
|
1328
|
+
if (customerRawPrint) {
|
|
1329
|
+
return {
|
|
1330
|
+
exitCode: execution.exitCode ?? CLI_EXIT_STATUS.success,
|
|
1331
|
+
suppressUpdateNotification: true,
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
if (machine) writeExecutionMachine(stdout, format, request.command, execution);
|
|
1335
|
+
else writeCommandHuman(stdout, stderr, request.capability, execution);
|
|
1336
|
+
return {
|
|
1337
|
+
exitCode: execution.exitCode,
|
|
1338
|
+
suppressUpdateNotification: machine || execution.exitCode !== CLI_EXIT_STATUS.success,
|
|
1339
|
+
};
|
|
1340
|
+
} catch (error) {
|
|
1341
|
+
const modeled = modeledError(error);
|
|
1342
|
+
if (machine) {
|
|
1343
|
+
writeMachine(stdout, format, errorEnvelope(activeCommand, modeled));
|
|
1344
|
+
} else {
|
|
1345
|
+
stderr.write(`${modeled.error.message}\n`);
|
|
1346
|
+
if (modeled.action?.command) stderr.write(`Next: ${modeled.action.command}\n`);
|
|
1347
|
+
}
|
|
1348
|
+
return { exitCode: modeled.exitCode, suppressUpdateNotification: true };
|
|
1349
|
+
}
|
|
1350
|
+
}
|