warpmetal 0.8.13 → 0.9.0
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 +49 -2
- package/package.json +2 -2
- package/skills/warpmetal/SKILL.md +7 -1
- package/skills/warpmetal/references/cli-reference.md +96 -0
- package/src/account-gateway.js +217 -0
- package/src/account-session.js +209 -0
- package/src/api.js +4 -0
- package/src/cli.js +308 -5
- package/src/customer-auth.js +329 -0
- package/src/models.js +85 -0
- package/src/runtime.js +76 -3
- package/src/state.js +20 -4
package/src/cli.js
CHANGED
|
@@ -10,12 +10,23 @@ import {
|
|
|
10
10
|
rejectUnknownOptions,
|
|
11
11
|
stringOption,
|
|
12
12
|
} from "./args.js";
|
|
13
|
+
import {
|
|
14
|
+
AccountGatewayClient,
|
|
15
|
+
accountSessionContext,
|
|
16
|
+
authenticatedAccountClient,
|
|
17
|
+
} from "./account-gateway.js";
|
|
13
18
|
import { WarpMetalClient } from "./api.js";
|
|
14
19
|
import { connectionProfile, writeConnectionProfile } from "./connection.js";
|
|
15
20
|
import { CliError, toErrorMessage } from "./errors.js";
|
|
16
21
|
import { establishHostTrust } from "./host-trust.js";
|
|
17
22
|
import { installSkill } from "./install-skill.js";
|
|
18
23
|
import { installRuntime } from "./installer.js";
|
|
24
|
+
import { openVerificationPage } from "./customer-auth.js";
|
|
25
|
+
import {
|
|
26
|
+
filterModelCatalog,
|
|
27
|
+
modelCatalogHuman,
|
|
28
|
+
MODEL_AUTH_MODES,
|
|
29
|
+
} from "./models.js";
|
|
19
30
|
import {
|
|
20
31
|
createPaymentRequestEnvelope,
|
|
21
32
|
defaultPaymentPaths,
|
|
@@ -74,12 +85,19 @@ const COMMON_OPTIONS = ["base-url", "json", "state-dir", "help"];
|
|
|
74
85
|
const HELP = `WarpMetal CLI ${VERSION}
|
|
75
86
|
|
|
76
87
|
Usage:
|
|
88
|
+
warpmetal login [--no-browser] [--read-only] [--json]
|
|
89
|
+
warpmetal auth status [--json]
|
|
90
|
+
warpmetal account orders [--task <taskId>] [--json]
|
|
91
|
+
warpmetal account devices [--server <serverId>] [--json]
|
|
92
|
+
warpmetal logout [--json]
|
|
77
93
|
warpmetal health [--json]
|
|
78
94
|
warpmetal catalog [--plan <planId>] [--json]
|
|
95
|
+
warpmetal models [--provider <id>] [--auth-mode <api_key|chatgpt_subscription>] [--json]
|
|
79
96
|
warpmetal order prepare --plan <planId> --hostname <name> --os <exact-name>
|
|
80
97
|
(--generate-ssh-key [--ssh-key-name <name>] | --ssh-public-key-file <path>)
|
|
81
98
|
[--runtime-file <path>] [--confirm TEMPORARY]
|
|
82
99
|
[--email <address>] [--idempotency-key <key>]
|
|
100
|
+
[--account [--without-agent-boxes | --without-team]]
|
|
83
101
|
warpmetal order status --task <taskId> [--wait] [--timeout-seconds <n>]
|
|
84
102
|
warpmetal checkout challenge --task <taskId> [--request-envelope-out <path>]
|
|
85
103
|
warpmetal checkout submit --task <taskId>
|
|
@@ -138,6 +156,8 @@ Usage:
|
|
|
138
156
|
|
|
139
157
|
Global options:
|
|
140
158
|
--base-url <url> Override https://api.warpmetal.com
|
|
159
|
+
--identity-url <url> Override https://identity.warpmetal.com for account auth
|
|
160
|
+
--account-url <url> Override https://warpmetal.com for account requests
|
|
141
161
|
--state-dir <path> Override the private state directory
|
|
142
162
|
--json Emit structured, secret-redacted JSON
|
|
143
163
|
--help Show help
|
|
@@ -827,6 +847,204 @@ async function handleCatalog(client, options, { json, stdout }) {
|
|
|
827
847
|
return 0;
|
|
828
848
|
}
|
|
829
849
|
|
|
850
|
+
async function handleModels(client, options, { json, stdout }) {
|
|
851
|
+
const provider = stringOption(options, "provider");
|
|
852
|
+
const authMode = stringOption(options, "auth-mode");
|
|
853
|
+
if (authMode !== undefined && !MODEL_AUTH_MODES.has(authMode)) {
|
|
854
|
+
throw new CliError(
|
|
855
|
+
"--auth-mode must be api_key or chatgpt_subscription.",
|
|
856
|
+
{ exitCode: 2 },
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
const result = await client.modelCatalog();
|
|
860
|
+
const data = filterModelCatalog(result.data, { provider, authMode });
|
|
861
|
+
emit(stdout, data, json, modelCatalogHuman(data));
|
|
862
|
+
return 0;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function accountOptions(options, context, stateDirectory) {
|
|
866
|
+
return {
|
|
867
|
+
stateDirectory,
|
|
868
|
+
identityOrigin: stringOption(options, "identity-url"),
|
|
869
|
+
accountOrigin: stringOption(options, "account-url"),
|
|
870
|
+
fetchImpl: context.fetchImpl,
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
function publicAccountIdentity(value) {
|
|
875
|
+
if (
|
|
876
|
+
!value ||
|
|
877
|
+
typeof value.principalId !== "string" ||
|
|
878
|
+
typeof value.email !== "string" ||
|
|
879
|
+
typeof value.emailVerified !== "boolean" ||
|
|
880
|
+
!Array.isArray(value.scopes) ||
|
|
881
|
+
value.scopes.some((scope) => !["cli:read", "cli:write"].includes(scope))
|
|
882
|
+
) {
|
|
883
|
+
throw new CliError("The account gateway returned an invalid identity response.", {
|
|
884
|
+
exitCode: 3,
|
|
885
|
+
code: "invalid_response",
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
return {
|
|
889
|
+
authenticated: true,
|
|
890
|
+
principalId: value.principalId,
|
|
891
|
+
email: value.email,
|
|
892
|
+
emailVerified: value.emailVerified,
|
|
893
|
+
scopes: value.scopes,
|
|
894
|
+
expiresAt:
|
|
895
|
+
typeof value.expiresAt === "string" ? value.expiresAt : undefined,
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
async function handleAccountLogin(options, context, stateDirectory) {
|
|
900
|
+
const readOnlyValue = options["read-only"];
|
|
901
|
+
if (readOnlyValue !== undefined && typeof readOnlyValue !== "boolean") {
|
|
902
|
+
throw new CliError("--read-only does not take a value.", { exitCode: 2 });
|
|
903
|
+
}
|
|
904
|
+
if (readOnlyValue === false) {
|
|
905
|
+
throw new CliError("Use --read-only to request a read-only session.", {
|
|
906
|
+
exitCode: 2,
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
if (options.browser === true) {
|
|
910
|
+
throw new CliError("Browser launch is automatic; use --no-browser to disable it.", {
|
|
911
|
+
exitCode: 2,
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
const account = accountSessionContext(
|
|
915
|
+
accountOptions(options, context, stateDirectory),
|
|
916
|
+
);
|
|
917
|
+
const scopes = readOnlyValue ? ["cli:read"] : ["cli:read", "cli:write"];
|
|
918
|
+
const controller = new AbortController();
|
|
919
|
+
const cancel = () => controller.abort();
|
|
920
|
+
process.once("SIGINT", cancel);
|
|
921
|
+
process.once("SIGTERM", cancel);
|
|
922
|
+
try {
|
|
923
|
+
const device = await account.auth.begin(scopes, {
|
|
924
|
+
signal: controller.signal,
|
|
925
|
+
});
|
|
926
|
+
writeLine(context.stderr, `Open ${device.verificationUriComplete}`);
|
|
927
|
+
writeLine(context.stderr, `Enter code: ${device.userCode}`);
|
|
928
|
+
if (options.browser !== false) {
|
|
929
|
+
openVerificationPage(device.verificationUriComplete, {
|
|
930
|
+
spawnImpl: context.spawnImpl,
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
const session = await account.auth.poll(device, {
|
|
934
|
+
signal: controller.signal,
|
|
935
|
+
});
|
|
936
|
+
const identity = publicAccountIdentity(
|
|
937
|
+
await new AccountGatewayClient({
|
|
938
|
+
accountOrigin: account.accountOrigin,
|
|
939
|
+
accessToken: session.accessToken,
|
|
940
|
+
fetchImpl: context.fetchImpl,
|
|
941
|
+
}).whoami(),
|
|
942
|
+
);
|
|
943
|
+
await account.store.withLock(async () => {
|
|
944
|
+
await account.store.write({
|
|
945
|
+
...session,
|
|
946
|
+
createdAt: new Date().toISOString(),
|
|
947
|
+
});
|
|
948
|
+
});
|
|
949
|
+
emit(
|
|
950
|
+
context.stdout,
|
|
951
|
+
identity,
|
|
952
|
+
context.json,
|
|
953
|
+
`Authenticated as ${identity.email} (${identity.scopes.join(", ")}).`,
|
|
954
|
+
);
|
|
955
|
+
return 0;
|
|
956
|
+
} finally {
|
|
957
|
+
process.removeListener("SIGINT", cancel);
|
|
958
|
+
process.removeListener("SIGTERM", cancel);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
async function handleAccountStatus(options, context, stateDirectory) {
|
|
963
|
+
const authenticated = await authenticatedAccountClient(
|
|
964
|
+
accountOptions(options, context, stateDirectory),
|
|
965
|
+
);
|
|
966
|
+
if (!authenticated) {
|
|
967
|
+
emit(
|
|
968
|
+
context.stdout,
|
|
969
|
+
{ authenticated: false },
|
|
970
|
+
context.json,
|
|
971
|
+
"Not authenticated. Run warpmetal login.",
|
|
972
|
+
);
|
|
973
|
+
return 0;
|
|
974
|
+
}
|
|
975
|
+
const identity = publicAccountIdentity(await authenticated.client.whoami());
|
|
976
|
+
emit(
|
|
977
|
+
context.stdout,
|
|
978
|
+
identity,
|
|
979
|
+
context.json,
|
|
980
|
+
`Authenticated as ${identity.email} (${identity.scopes.join(", ")}).`,
|
|
981
|
+
);
|
|
982
|
+
return 0;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
async function requireAccount(options, context, stateDirectory) {
|
|
986
|
+
const account = await authenticatedAccountClient(accountOptions(options, context, stateDirectory));
|
|
987
|
+
if (!account) {
|
|
988
|
+
throw new CliError("Sign in first with warpmetal login; no order details are needed to sign in.", { exitCode: 4 });
|
|
989
|
+
}
|
|
990
|
+
return account;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
async function handleAccountInventory(kind, options, context, stateDirectory) {
|
|
994
|
+
const account = await requireAccount(options, context, stateDirectory);
|
|
995
|
+
const id = stringOption(options, kind === "orders" ? "task" : "server");
|
|
996
|
+
const data = kind === "orders"
|
|
997
|
+
? await (id ? account.client.order(id) : account.client.orders())
|
|
998
|
+
: await (id ? account.client.device(id) : account.client.devices());
|
|
999
|
+
emit(context.stdout, data, context.json, JSON.stringify(data, null, 2));
|
|
1000
|
+
return 0;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
async function handleAccountLogout(options, context, stateDirectory) {
|
|
1004
|
+
const account = accountSessionContext(
|
|
1005
|
+
accountOptions(options, context, stateDirectory),
|
|
1006
|
+
);
|
|
1007
|
+
const outcome = await account.store.withLock(async () => {
|
|
1008
|
+
const session = await account.store.read();
|
|
1009
|
+
if (!session) {
|
|
1010
|
+
return {
|
|
1011
|
+
authenticated: false,
|
|
1012
|
+
localSessionCleared: false,
|
|
1013
|
+
remoteRevoked: false,
|
|
1014
|
+
remoteStatus: "no_session",
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
let remoteRevoked = false;
|
|
1018
|
+
let remoteError;
|
|
1019
|
+
try {
|
|
1020
|
+
await account.auth.revoke(session.refreshToken);
|
|
1021
|
+
remoteRevoked = true;
|
|
1022
|
+
} catch (error) {
|
|
1023
|
+
remoteError = error?.code || "unavailable";
|
|
1024
|
+
} finally {
|
|
1025
|
+
await account.store.clear();
|
|
1026
|
+
}
|
|
1027
|
+
return {
|
|
1028
|
+
authenticated: false,
|
|
1029
|
+
localSessionCleared: true,
|
|
1030
|
+
remoteRevoked,
|
|
1031
|
+
remoteStatus: remoteRevoked ? "revoked" : "failed",
|
|
1032
|
+
remoteError,
|
|
1033
|
+
};
|
|
1034
|
+
});
|
|
1035
|
+
emit(
|
|
1036
|
+
context.stdout,
|
|
1037
|
+
outcome,
|
|
1038
|
+
context.json,
|
|
1039
|
+
outcome.remoteRevoked
|
|
1040
|
+
? "Logged out and revoked the remote session."
|
|
1041
|
+
: outcome.remoteStatus === "no_session"
|
|
1042
|
+
? "No account session is stored for these origins."
|
|
1043
|
+
: "Local session cleared, but remote revocation could not be confirmed.",
|
|
1044
|
+
);
|
|
1045
|
+
return outcome.remoteStatus === "failed" ? 3 : 0;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
830
1048
|
async function pathExists(path) {
|
|
831
1049
|
try {
|
|
832
1050
|
await stat(path);
|
|
@@ -898,6 +1116,20 @@ async function resolveServerIdentity(store, serverId, explicit) {
|
|
|
898
1116
|
}
|
|
899
1117
|
|
|
900
1118
|
async function handlePrepareOrder(client, store, options, context) {
|
|
1119
|
+
const accountMode = booleanOption(options, "account");
|
|
1120
|
+
const account = accountMode ? await requireAccount(options, context, store.directory) : null;
|
|
1121
|
+
const identity = account ? publicAccountIdentity(await account.client.whoami()) : null;
|
|
1122
|
+
if (account && !account.session.scopes.includes("cli:write")) {
|
|
1123
|
+
throw new CliError("This account session is read-only. Run warpmetal login to request order preparation.", { exitCode: 4 });
|
|
1124
|
+
}
|
|
1125
|
+
const withoutBoxes = booleanOption(options, "without-agent-boxes");
|
|
1126
|
+
const withoutTeam = booleanOption(options, "without-team");
|
|
1127
|
+
if (!account && (withoutBoxes || withoutTeam || options["identity-url"] || options["account-url"])) {
|
|
1128
|
+
throw new CliError("Account options require --account. Existing owner-token preparation is unchanged.", { exitCode: 2 });
|
|
1129
|
+
}
|
|
1130
|
+
if (withoutBoxes && withoutTeam) {
|
|
1131
|
+
throw new CliError("Use only one of --without-agent-boxes or --without-team.", { exitCode: 2 });
|
|
1132
|
+
}
|
|
901
1133
|
const planId = stringOption(options, "plan", { required: true });
|
|
902
1134
|
const hostname = stringOption(options, "hostname", { required: true });
|
|
903
1135
|
const osName = stringOption(options, "os", { required: true });
|
|
@@ -910,10 +1142,16 @@ async function handlePrepareOrder(client, store, options, context) {
|
|
|
910
1142
|
);
|
|
911
1143
|
}
|
|
912
1144
|
const email = stringOption(options, "email");
|
|
1145
|
+
if (account && email) throw new CliError("Account contact comes from your signed-in account; omit --email.", { exitCode: 2 });
|
|
913
1146
|
const runtimeFile = stringOption(options, "runtime-file");
|
|
1147
|
+
if (runtimeFile && withoutBoxes) throw new CliError("--runtime-file conflicts with --without-agent-boxes.", { exitCode: 2 });
|
|
1148
|
+
if (account && !runtimeFile && !withoutBoxes && !withoutTeam) {
|
|
1149
|
+
throw new CliError("Agent Boxes and Agent team are enabled by default. Select your team in --runtime-file, or use --without-team / --without-agent-boxes.", { exitCode: 2 });
|
|
1150
|
+
}
|
|
914
1151
|
const runtimeIntent = runtimeFile
|
|
915
1152
|
? await readSandboxFile(runtimeFile)
|
|
916
|
-
: undefined;
|
|
1153
|
+
: account && withoutTeam ? [{ name: "main", size: "small" }] : undefined;
|
|
1154
|
+
if (withoutTeam && runtimeIntent?.teams) throw new CliError("--without-team conflicts with teams in --runtime-file.", { exitCode: 2 });
|
|
917
1155
|
const sandboxes = Array.isArray(runtimeIntent)
|
|
918
1156
|
? runtimeIntent
|
|
919
1157
|
: runtimeIntent?.sandboxes;
|
|
@@ -969,14 +1207,22 @@ async function handlePrepareOrder(client, store, options, context) {
|
|
|
969
1207
|
}
|
|
970
1208
|
const key =
|
|
971
1209
|
stringOption(options, "idempotency-key") || idempotencyKey("order");
|
|
972
|
-
const response =
|
|
1210
|
+
const response = account
|
|
1211
|
+
? { data: await account.client.prepareOrder(request, key) }
|
|
1212
|
+
: await client.prepareOrder(request, key);
|
|
973
1213
|
const checkoutBody = JSON.stringify({ taskId: response.data.task.id });
|
|
974
1214
|
await store.savePreparedOrder(
|
|
975
1215
|
response.data,
|
|
976
1216
|
checkoutBody,
|
|
977
1217
|
selected.identity.identityId,
|
|
1218
|
+
account ? { principalId: identity.principalId, accountOrigin: account.context.accountOrigin, identityOrigin: account.context.identityOrigin } : null,
|
|
978
1219
|
);
|
|
979
1220
|
const safe = safePreparedOrder(response.data, store.path);
|
|
1221
|
+
if (account) {
|
|
1222
|
+
safe.ownershipMode = "account";
|
|
1223
|
+
safe.credential = { kind: "account_session", stored: true, printed: false };
|
|
1224
|
+
safe.stateFile = store.path;
|
|
1225
|
+
}
|
|
980
1226
|
if (runtimeIntent) {
|
|
981
1227
|
safe.agentRuntimeIntent = {
|
|
982
1228
|
sandboxes: sandboxes.map((sandbox) => ({
|
|
@@ -988,13 +1234,16 @@ async function handlePrepareOrder(client, store, options, context) {
|
|
|
988
1234
|
};
|
|
989
1235
|
if (!Array.isArray(runtimeIntent)) {
|
|
990
1236
|
safe.agentRuntimeIntent.setup = runtimeIntent.setup;
|
|
1237
|
+
safe.agentRuntimeIntent.teams = runtimeIntent.teams;
|
|
991
1238
|
}
|
|
992
1239
|
}
|
|
993
1240
|
emit(
|
|
994
1241
|
context.stdout,
|
|
995
1242
|
safe,
|
|
996
1243
|
context.json,
|
|
997
|
-
|
|
1244
|
+
account
|
|
1245
|
+
? `Prepared ${safe.task.id} for your account. Ownership recorded in ${store.path}. No owner token was issued. Use warpmetal account orders --task ${safe.task.id} to view it.`
|
|
1246
|
+
: `Prepared ${safe.task.id} for server ${safe.task.serverId}.\nRecovery credential saved to ${store.path} and not printed.`,
|
|
998
1247
|
);
|
|
999
1248
|
return 0;
|
|
1000
1249
|
}
|
|
@@ -2431,7 +2680,13 @@ async function resolveCreateSandboxes(options) {
|
|
|
2431
2680
|
exitCode: 2,
|
|
2432
2681
|
});
|
|
2433
2682
|
}
|
|
2434
|
-
if (file)
|
|
2683
|
+
if (file) {
|
|
2684
|
+
const intent = await readSandboxFile(file);
|
|
2685
|
+
if (!Array.isArray(intent)) {
|
|
2686
|
+
throw new CliError("Team and setup intents are supported only during order preparation; sandbox create accepts sandboxes only.", { exitCode: 2 });
|
|
2687
|
+
}
|
|
2688
|
+
return intent;
|
|
2689
|
+
}
|
|
2435
2690
|
return sandboxFromOptions({
|
|
2436
2691
|
name: stringOption(options, "name", { required: true }),
|
|
2437
2692
|
size: stringOption(options, "size", { required: true }),
|
|
@@ -3068,10 +3323,46 @@ async function dispatch(positionals, options, passthrough, context) {
|
|
|
3068
3323
|
return handleAccessRemoveSsh(options, context);
|
|
3069
3324
|
}
|
|
3070
3325
|
|
|
3071
|
-
const baseUrl = stringOption(options, "base-url");
|
|
3072
3326
|
const stateDir =
|
|
3073
3327
|
stringOption(options, "state-dir") ||
|
|
3074
3328
|
resolveStateDirectory({ env: context.env });
|
|
3329
|
+
if (command === "login") {
|
|
3330
|
+
rejectUnknownOptions(options, [
|
|
3331
|
+
"base-url",
|
|
3332
|
+
"json",
|
|
3333
|
+
"help",
|
|
3334
|
+
"state-dir",
|
|
3335
|
+
"identity-url",
|
|
3336
|
+
"account-url",
|
|
3337
|
+
"browser",
|
|
3338
|
+
"read-only",
|
|
3339
|
+
]);
|
|
3340
|
+
return handleAccountLogin(options, context, stateDir);
|
|
3341
|
+
}
|
|
3342
|
+
if (command === "auth status") {
|
|
3343
|
+
rejectUnknownOptions(options, [
|
|
3344
|
+
"base-url",
|
|
3345
|
+
"json",
|
|
3346
|
+
"help",
|
|
3347
|
+
"state-dir",
|
|
3348
|
+
"identity-url",
|
|
3349
|
+
"account-url",
|
|
3350
|
+
]);
|
|
3351
|
+
return handleAccountStatus(options, context, stateDir);
|
|
3352
|
+
}
|
|
3353
|
+
if (command === "logout") {
|
|
3354
|
+
rejectUnknownOptions(options, [
|
|
3355
|
+
"base-url",
|
|
3356
|
+
"json",
|
|
3357
|
+
"help",
|
|
3358
|
+
"state-dir",
|
|
3359
|
+
"identity-url",
|
|
3360
|
+
"account-url",
|
|
3361
|
+
]);
|
|
3362
|
+
return handleAccountLogout(options, context, stateDir);
|
|
3363
|
+
}
|
|
3364
|
+
|
|
3365
|
+
const baseUrl = stringOption(options, "base-url");
|
|
3075
3366
|
const client = new WarpMetalClient({
|
|
3076
3367
|
baseUrl: baseUrl || context.env.WARPMETAL_API_URL,
|
|
3077
3368
|
fetchImpl: context.fetchImpl,
|
|
@@ -3085,9 +3376,21 @@ async function dispatch(positionals, options, passthrough, context) {
|
|
|
3085
3376
|
case "catalog":
|
|
3086
3377
|
rejectUnknownOptions(options, [...COMMON_OPTIONS, "plan"]);
|
|
3087
3378
|
return handleCatalog(client, options, context);
|
|
3379
|
+
case "account orders":
|
|
3380
|
+
case "account devices":
|
|
3381
|
+
rejectUnknownOptions(options, [...COMMON_OPTIONS, "identity-url", "account-url", command === "account orders" ? "task" : "server"]);
|
|
3382
|
+
return handleAccountInventory(command === "account orders" ? "orders" : "devices", options, context, stateDir);
|
|
3383
|
+
case "models":
|
|
3384
|
+
rejectUnknownOptions(options, [...COMMON_OPTIONS, "provider", "auth-mode"]);
|
|
3385
|
+
return handleModels(client, options, context);
|
|
3088
3386
|
case "order prepare":
|
|
3089
3387
|
rejectUnknownOptions(options, [
|
|
3090
3388
|
...COMMON_OPTIONS,
|
|
3389
|
+
"account",
|
|
3390
|
+
"identity-url",
|
|
3391
|
+
"account-url",
|
|
3392
|
+
"without-agent-boxes",
|
|
3393
|
+
"without-team",
|
|
3091
3394
|
"plan",
|
|
3092
3395
|
"hostname",
|
|
3093
3396
|
"os",
|