@runtypelabs/cli 2.59.1 → 2.59.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/chunk-0x7UKC1U.js +17 -0
  2. package/dist/chunk-2ySxXtcg.js +418 -0
  3. package/dist/chunk-3An_BRHv.js +501 -0
  4. package/dist/chunk-6uTxgdiY.js +16218 -0
  5. package/dist/chunk-87nRnNRG.js +1363 -0
  6. package/dist/chunk-B3wo8uRx.js +322 -0
  7. package/dist/chunk-BGp3ZdBy.js +560 -0
  8. package/dist/chunk-BJQv9Elp.js +107 -0
  9. package/dist/chunk-BOpO6xE-.js +6053 -0
  10. package/dist/chunk-BQXXCL8B.js +102 -0
  11. package/dist/chunk-BV-8isOd.js +34 -0
  12. package/dist/chunk-BVMhjGVe.js +826 -0
  13. package/dist/chunk-BW2fg46T.js +141 -0
  14. package/dist/chunk-BWJdY5Ag.js +36 -0
  15. package/dist/chunk-BYJOrLhh.js +302 -0
  16. package/dist/chunk-BYab8EVL.js +127 -0
  17. package/dist/chunk-BfEKvp7h.js +82 -0
  18. package/dist/chunk-BhgLSfH6.js +1123 -0
  19. package/dist/chunk-BirUxTtH.js +296 -0
  20. package/dist/chunk-BmB6UVZ9.js +1559 -0
  21. package/dist/chunk-BnWhIqW1.js +109 -0
  22. package/dist/chunk-Bpfnfk0t.js +123 -0
  23. package/dist/chunk-BrhgdnM3.js +108 -0
  24. package/dist/chunk-Bt0XAgX5.js +327 -0
  25. package/dist/chunk-Bu0FJr6S.js +84 -0
  26. package/dist/chunk-BvEoKpN3.js +255 -0
  27. package/dist/chunk-BxNJgYbE.js +28 -0
  28. package/dist/chunk-C1OFhd7F.js +6131 -0
  29. package/dist/chunk-C7e7WwbU.js +331 -0
  30. package/dist/chunk-CDTYP6bN.js +558 -0
  31. package/dist/chunk-CGDQoqDC.js +24 -0
  32. package/dist/chunk-CJraEBVd.js +127 -0
  33. package/dist/chunk-CQrNhOtv.js +40 -0
  34. package/dist/chunk-CW6AzBH6.js +128 -0
  35. package/dist/chunk-CXgL4w-9.js +317 -0
  36. package/dist/chunk-CZrqoPyf.js +233 -0
  37. package/dist/chunk-CcFCyqbB.js +83 -0
  38. package/dist/chunk-CdaIvO-y.js +485 -0
  39. package/dist/chunk-CehSXtgK.js +624 -0
  40. package/dist/chunk-Ciqkop65.js +218 -0
  41. package/dist/chunk-CoAWdSiE.js +43 -0
  42. package/dist/chunk-Crb4y7t4.js +830 -0
  43. package/dist/chunk-CrlqIfmb.js +337 -0
  44. package/dist/chunk-Cs4pwCQ-.js +324 -0
  45. package/dist/chunk-CwM_9y01.js +519 -0
  46. package/dist/chunk-CyRbIJg8.js +440 -0
  47. package/dist/chunk-D0OHsyMy.js +212 -0
  48. package/dist/chunk-D3y2bURs.js +1347 -0
  49. package/dist/chunk-D4_mJk03.js +2338 -0
  50. package/dist/chunk-D6C8rned.js +578 -0
  51. package/dist/chunk-D9ZbUnks.js +11 -0
  52. package/dist/chunk-DEPmLshX.js +834 -0
  53. package/dist/chunk-DFj1UPB2.js +330 -0
  54. package/dist/chunk-DGdFdZVf.js +197 -0
  55. package/dist/chunk-DGv7X9S_.js +63 -0
  56. package/dist/chunk-DJQ47WYZ.js +664 -0
  57. package/dist/chunk-DJw3O61j.js +1974 -0
  58. package/dist/chunk-DK4FbWVK.js +100 -0
  59. package/dist/chunk-DPuofXMp.js +3907 -0
  60. package/dist/chunk-DSRYhUus.js +993 -0
  61. package/dist/chunk-DajZ9Eg4.js +299 -0
  62. package/dist/chunk-Df4dunvU.js +195 -0
  63. package/dist/chunk-DjNTPhfO.js +481 -0
  64. package/dist/chunk-Dn8eaJuu.js +1042 -0
  65. package/dist/chunk-DwDWqSxM.js +637 -0
  66. package/dist/chunk-Dz7cEtxl.js +84 -0
  67. package/dist/chunk-E_0bgjrS.js +359 -0
  68. package/dist/chunk-KPYN2vUp.js +776 -0
  69. package/dist/chunk-KcqPQJBi.js +86 -0
  70. package/dist/chunk-N9H0JnoG.js +349 -0
  71. package/dist/chunk-NONFcXkn.js +728 -0
  72. package/dist/chunk-PrFjcpmH.js +256 -0
  73. package/dist/chunk-QSfNYiw9.js +11 -0
  74. package/dist/chunk-R265bzj9.js +226 -0
  75. package/dist/chunk-X9V_r6jM.js +166 -0
  76. package/dist/chunk-ZBtPUtyA.js +55 -0
  77. package/dist/chunk-ZCTfmOs7.js +4939 -0
  78. package/dist/chunk-h6oavj0t.js +97 -0
  79. package/dist/chunk-i7DwkvvD.js +42 -0
  80. package/dist/chunk-mG-4eZ6S.js +203 -0
  81. package/dist/chunk-o0ygUbCf.js +313 -0
  82. package/dist/chunk-ucu5lrDZ.js +5715 -0
  83. package/dist/index.js +11367 -73789
  84. package/package.json +5 -5
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+ import { F as getCliVersion, l as normalizeApiUrl, o as getEffectiveApiUrl, r as assertCliClientTarget } from "./index.js";
3
+ import { RuntypeApiError, createClient } from "@runtypelabs/sdk";
4
+ // src/config/env.ts
5
+ function getApiUrl() {
6
+ return getEffectiveApiUrl();
7
+ }
8
+ function getApiVersion() {
9
+ return process.env.RUNTYPE_API_VERSION || "v1";
10
+ }
11
+ function getDashboardUrl() {
12
+ return process.env.RUNTYPE_DASHBOARD_URL || "https://use.runtype.com";
13
+ }
14
+ function getClerkPublishableKey() {
15
+ return process.env.CLERK_PUBLISHABLE_KEY || "pk_live_default_key";
16
+ }
17
+ function getRuntimeApiKey() {
18
+ return process.env.RUNTYPE_API_KEY;
19
+ }
20
+ function getDevPlanOverride() {
21
+ return process.env.RUNTYPE_DEV_PLAN_OVERRIDE;
22
+ }
23
+ // src/lib/sdk-client.ts
24
+ /**
25
+ * Construct the SDK client used by every CLI command for API access.
26
+ * The SDK is the single source of truth for the API surface; do NOT reintroduce raw fetch (enforced by `runtype/cli-use-sdk`).
27
+ * Reproduces the headers the CLI has always sent: client identification, versioned User-Agent, and optional dev plan override.
28
+ */
29
+ function createCliClient(apiKey, baseUrl) {
30
+ const devOverride = getDevPlanOverride();
31
+ const resolvedApiUrl = normalizeApiUrl(baseUrl || getApiUrl());
32
+ assertCliClientTarget(apiKey, resolvedApiUrl);
33
+ return createClient({
34
+ apiKey,
35
+ baseUrl: resolvedApiUrl,
36
+ apiVersion: getApiVersion(),
37
+ timeout: null,
38
+ headers: {
39
+ "X-Runtype-Client": "cli",
40
+ "User-Agent": `runtype-cli/${getCliVersion()}`,
41
+ "X-Runtype-CLI-Version": getCliVersion(),
42
+ ...devOverride ? { "X-Dev-Plan-Override": devOverride } : {}
43
+ }
44
+ });
45
+ }
46
+ // src/auth/api-key-manager.ts
47
+ const isCliTokenResponse = (value) => {
48
+ if (!value || typeof value !== "object") return false;
49
+ const record = value;
50
+ return typeof record.apiKey === "string" && typeof record.userId === "string";
51
+ };
52
+ const isAuthMeResponse = (value) => {
53
+ if (!value || typeof value !== "object") return false;
54
+ return typeof value.userId === "string";
55
+ };
56
+ /**
57
+ * The key could not be checked at all — nothing on the path ever said the
58
+ * credentials were bad. Kept distinct from `validateApiKey` returning false so
59
+ * callers stop telling people their key is invalid when the real problem is
60
+ * that an egress proxy, firewall, or outage sits between them and the API.
61
+ */
62
+ var ApiKeyValidationUnavailableError = class extends Error {
63
+ statusCode;
64
+ constructor(message, statusCode, options) {
65
+ super(message, options);
66
+ this.statusCode = statusCode;
67
+ this.name = "ApiKeyValidationUnavailableError";
68
+ }
69
+ };
70
+ const describeUnverifiableStatus = (statusCode, apiUrl) => {
71
+ const answered = `Could not verify the API key: the API at ${apiUrl} answered HTTP ${statusCode}`;
72
+ if (statusCode === 403 || statusCode === 407) return `${answered} (this can be an egress proxy or firewall blocking ${apiUrl}, not an invalid key)`;
73
+ if (statusCode >= 500) return `${answered} (the API or something in front of it is failing, not an invalid key). Try again shortly.`;
74
+ return `${answered}, which is neither a success nor a credential rejection, so the key was not verified.`;
75
+ };
76
+ var ApiKeyManager = class {
77
+ /**
78
+ * Exchange a Clerk session JWT for a persistent API key.
79
+ *
80
+ * Calls POST /v1/auth/cli-token on the Runtype API directly,
81
+ * passing the Clerk JWT as a Bearer token. The API verifies the JWT
82
+ * with Clerk and creates an API key for the CLI.
83
+ */
84
+ async exchangeSessionForApiKey(clerkJwt, apiUrl) {
85
+ const client = createCliClient(clerkJwt, apiUrl);
86
+ let data;
87
+ try {
88
+ data = await client.post("/auth/cli-token");
89
+ } catch (error) {
90
+ const message = error instanceof Error ? error.message : String(error);
91
+ throw new Error(`Authentication failed: ${message}`, { cause: error });
92
+ }
93
+ if (!isCliTokenResponse(data)) throw new Error("Invalid authentication response format");
94
+ const result = {
95
+ key: data.apiKey,
96
+ userId: data.userId
97
+ };
98
+ if (data.orgId) result.orgId = data.orgId;
99
+ return result;
100
+ }
101
+ /**
102
+ * Resolve whether the API accepts this key.
103
+ * `false` means the API explicitly rejected the credentials; anything unresolved throws.
104
+ * Returning false for every non-2xx response used to misreport a proxy 403/407 or API 5xx as an invalid key.
105
+ */
106
+ async validateApiKey(apiKey, apiUrl) {
107
+ const resolvedApiUrl = apiUrl || getApiUrl();
108
+ const client = createCliClient(apiKey, resolvedApiUrl);
109
+ try {
110
+ await client.get("/users/profile");
111
+ return true;
112
+ } catch (error) {
113
+ if (error instanceof RuntypeApiError) {
114
+ if (error.statusCode === 401) return false;
115
+ throw new ApiKeyValidationUnavailableError(describeUnverifiableStatus(error.statusCode, resolvedApiUrl), error.statusCode, { cause: error });
116
+ }
117
+ throw error;
118
+ }
119
+ }
120
+ async getCurrentUser(apiKey, apiUrl) {
121
+ const client = createCliClient(apiKey, apiUrl);
122
+ let data;
123
+ try {
124
+ data = await client.get("/auth/me");
125
+ } catch {
126
+ try {
127
+ const profile = await client.get("/users/profile");
128
+ if (typeof profile.id === "string") data = {
129
+ userId: profile.id,
130
+ orgId: profile.orgId ?? null,
131
+ email: profile.email
132
+ };
133
+ } catch {}
134
+ if (!data) throw new Error("Failed to get user information");
135
+ }
136
+ if (!isAuthMeResponse(data)) throw new Error("Invalid user response format");
137
+ return data;
138
+ }
139
+ };
140
+ export { getApiVersion as a, getDevPlanOverride as c, getApiUrl as i, getRuntimeApiKey as l, ApiKeyValidationUnavailableError as n, getClerkPublishableKey as o, createCliClient as r, getDashboardUrl as s, ApiKeyManager as t };
141
+
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import { v as managedDeploymentCreateRequestSchema } from "./chunk-ucu5lrDZ.js";
3
+ import { r as createCliClient } from "./chunk-BW2fg46T.js";
4
+ import { n as ensureAuth } from "./chunk-CJraEBVd.js";
5
+ import { a as printJson } from "./chunk-CcFCyqbB.js";
6
+ import { Command } from "commander";
7
+ import { readFile } from "node:fs/promises";
8
+ // src/commands/sandbox-deployments.ts
9
+ const sandboxDeploymentsCommand = new Command("sandbox-deployments").description("Author managed deployments using retained runtime profiles and uploaded source IDs");
10
+ async function run(action) {
11
+ try {
12
+ const apiKey = await ensureAuth();
13
+ if (!apiKey) return;
14
+ printJson(await action(createCliClient(apiKey)));
15
+ } catch (error) {
16
+ console.error(error instanceof Error ? error.message : String(error));
17
+ process.exitCode = 1;
18
+ }
19
+ }
20
+ sandboxDeploymentsCommand.command("runtime-profiles").description("List retained profiles and admission information for this environment").action(() => run((client) => client.sandboxDeployments.runtimeProfiles()));
21
+ sandboxDeploymentsCommand.command("definition <id>").description("Read canonical config and advanced spec from the accepted revision").action((id) => run((client) => client.sandboxDeployments.definition(id)));
22
+ sandboxDeploymentsCommand.command("create").description("Accept a config or advanced spec; the response does not indicate readiness").requiredOption("-f, --file <path>", "JSON request containing exactly one of config or spec").requiredOption("--idempotency-key <key>", "Reuse this key and file when retrying the request").action((options) => run(async (client) => {
23
+ const request = JSON.parse(await readFile(options.file, "utf8"));
24
+ managedDeploymentCreateRequestSchema.parse(request);
25
+ return client.sandboxDeployments.create(request, { idempotencyKey: options.idempotencyKey });
26
+ }));
27
+ sandboxDeploymentsCommand.command("update <id>").description("Replace the complete config or spec without renewing retention").requiredOption("-f, --file <path>", "JSON request containing exactly one of config or spec").requiredOption("--idempotency-key <key>", "Reuse this key and file when retrying the request").requiredOption("--if-match <version>", "Resource version from deployment definition readback").action((id, options) => run(async (client) => {
28
+ const request = JSON.parse(await readFile(options.file, "utf8"));
29
+ managedDeploymentCreateRequestSchema.parse(request);
30
+ return client.sandboxDeployments.update(id, request, {
31
+ idempotencyKey: options.idempotencyKey,
32
+ ifMatch: options.ifMatch
33
+ });
34
+ }));
35
+ export { sandboxDeploymentsCommand };
36
+
@@ -0,0 +1,302 @@
1
+ #!/usr/bin/env node
2
+ import { t as isTTY } from "./index.js";
3
+ import { DataList, EntityCard, MutationResult, React, render, useEffect, useState } from "./chunk-CoAWdSiE.js";
4
+ import { r as createCliClient } from "./chunk-BW2fg46T.js";
5
+ import { n as ensureAuth } from "./chunk-CJraEBVd.js";
6
+ import { a as printJson, r as getTotalCount } from "./chunk-CcFCyqbB.js";
7
+ import { Command } from "commander";
8
+ import chalk from "chalk";
9
+ // src/commands/models.ts
10
+ const modelsCommand = new Command("models").description("Manage model configurations");
11
+ modelsCommand.command("list").description("List your enabled model configurations").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
12
+ const apiKey = await ensureAuth();
13
+ if (!apiKey) return;
14
+ const client = createCliClient(apiKey);
15
+ if (!isTTY(options) || options.json) {
16
+ try {
17
+ const data = await client.get("/model-configs");
18
+ if (options.json) {
19
+ printJson(data);
20
+ return;
21
+ }
22
+ const models = data.data ?? [];
23
+ if (models.length === 0) {
24
+ console.log("No model configurations found");
25
+ return;
26
+ }
27
+ console.log("Your Models:");
28
+ for (const model of models) {
29
+ const defaultTag = model.isDefault ? " (default)" : "";
30
+ const statusTag = model.enabled === false ? " [disabled]" : "";
31
+ console.log(` ${model.id} ${model.modelId}${defaultTag}${statusTag}`);
32
+ }
33
+ const total = getTotalCount(data.pagination);
34
+ if (total !== void 0) console.log(`\n Total: ${total} models`);
35
+ } catch (error) {
36
+ const message = error instanceof Error ? error.message : "Unknown error";
37
+ console.error(chalk.red(`Failed to fetch models: ${message}`));
38
+ process.exit(1);
39
+ }
40
+ return;
41
+ }
42
+ const App = () => {
43
+ const [items, setItems] = useState(null);
44
+ const [error, setError] = useState(null);
45
+ const [total, setTotal] = useState();
46
+ useEffect(() => {
47
+ client.get("/model-configs").then((res) => {
48
+ setItems(res.data ?? []);
49
+ setTotal(getTotalCount(res.pagination));
50
+ }).catch((err) => setError(err instanceof Error ? err : new Error(String(err))));
51
+ }, []);
52
+ return React.createElement(DataList, {
53
+ title: "Your Models",
54
+ items,
55
+ error,
56
+ loading: items === null && error === null,
57
+ total,
58
+ renderCard: (item) => {
59
+ const model = item;
60
+ return React.createElement(EntityCard, { fields: [
61
+ {
62
+ label: "ID",
63
+ value: model.id,
64
+ color: "green"
65
+ },
66
+ {
67
+ label: "Model",
68
+ value: model.modelId
69
+ },
70
+ {
71
+ label: "Default",
72
+ value: model.isDefault ? "yes" : null,
73
+ color: "yellow"
74
+ },
75
+ {
76
+ label: "Enabled",
77
+ value: model.enabled === false ? "no" : null
78
+ }
79
+ ] });
80
+ },
81
+ emptyMessage: "No model configurations found"
82
+ });
83
+ };
84
+ const { waitUntilExit } = render(React.createElement(App));
85
+ await waitUntilExit();
86
+ });
87
+ modelsCommand.command("available").description("List all available models grouped by provider").option("--search <query>", "Only models matching every word, ordered by relevance").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
88
+ const apiKey = await ensureAuth();
89
+ if (!apiKey) return;
90
+ const client = createCliClient(apiKey);
91
+ const groupedPath = options.search ? `/model-configs/grouped?q=${encodeURIComponent(options.search)}` : "/model-configs/grouped";
92
+ if (!isTTY(options) || options.json) {
93
+ try {
94
+ const data = await client.get(groupedPath);
95
+ if (options.json) {
96
+ printJson(data);
97
+ return;
98
+ }
99
+ const groups = data.data ?? [];
100
+ if (groups.length === 0) {
101
+ console.log("No models available");
102
+ return;
103
+ }
104
+ console.log("Available Models:");
105
+ for (const group of groups) {
106
+ console.log(`\n ${group.provider} / ${group.baseModel}`);
107
+ for (const variant of group.variants) {
108
+ const configuredTag = variant.configured ? " [configured]" : "";
109
+ console.log(` ${variant.modelId}${configuredTag}`);
110
+ }
111
+ }
112
+ } catch (error) {
113
+ const message = error instanceof Error ? error.message : "Unknown error";
114
+ console.error(chalk.red(`Failed to fetch available models: ${message}`));
115
+ process.exit(1);
116
+ }
117
+ return;
118
+ }
119
+ const App = () => {
120
+ const [items, setItems] = useState(null);
121
+ const [error, setError] = useState(null);
122
+ useEffect(() => {
123
+ client.get(groupedPath).then((res) => setItems(res.data ?? [])).catch((err) => setError(err instanceof Error ? err : new Error(String(err))));
124
+ }, []);
125
+ return React.createElement(DataList, {
126
+ title: "Available Models",
127
+ items,
128
+ error,
129
+ loading: items === null && error === null,
130
+ renderCard: (item) => {
131
+ const group = item;
132
+ return React.createElement(EntityCard, {
133
+ title: `${group.provider} / ${group.baseModel}`,
134
+ fields: group.variants.map((v) => ({
135
+ label: v.modelId,
136
+ value: v.configured ? "configured" : "available",
137
+ color: v.configured ? "green" : void 0
138
+ }))
139
+ });
140
+ },
141
+ emptyMessage: "No models available"
142
+ });
143
+ };
144
+ const { waitUntilExit } = render(React.createElement(App));
145
+ await waitUntilExit();
146
+ });
147
+ modelsCommand.command("enable <modelId>").description("Enable a model by creating a configuration").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (modelId, options) => {
148
+ const apiKey = await ensureAuth();
149
+ if (!apiKey) return;
150
+ const client = createCliClient(apiKey);
151
+ if (!isTTY(options) || options.json) {
152
+ try {
153
+ const data = await client.post("/model-configs", { modelId });
154
+ if (options.json) {
155
+ printJson(data);
156
+ return;
157
+ }
158
+ console.log(` Config ID: ${data.id}`);
159
+ console.log(` Model: ${data.modelId}`);
160
+ } catch (error) {
161
+ const message = error instanceof Error ? error.message : "Unknown error";
162
+ console.error(chalk.red(`Failed to enable model: ${message}`));
163
+ process.exit(1);
164
+ }
165
+ return;
166
+ }
167
+ const App = () => {
168
+ const [loading, setLoading] = useState(true);
169
+ const [success, setSuccess] = useState(null);
170
+ const [error, setError] = useState(null);
171
+ const [result, setResult] = useState(null);
172
+ useEffect(() => {
173
+ client.post("/model-configs", { modelId }).then((data) => {
174
+ setResult(data);
175
+ setSuccess(true);
176
+ setLoading(false);
177
+ }).catch((err) => {
178
+ setError(err instanceof Error ? err : new Error(String(err)));
179
+ setSuccess(false);
180
+ setLoading(false);
181
+ });
182
+ }, []);
183
+ return React.createElement(MutationResult, {
184
+ loading,
185
+ loadingLabel: `Enabling model ${modelId}...`,
186
+ success,
187
+ successMessage: "Model enabled",
188
+ error,
189
+ result: result ? React.createElement(EntityCard, { fields: [{
190
+ label: "ID",
191
+ value: result.id,
192
+ color: "green"
193
+ }, {
194
+ label: "Model",
195
+ value: result.modelId
196
+ }] }) : void 0
197
+ });
198
+ };
199
+ const { waitUntilExit } = render(React.createElement(App));
200
+ await waitUntilExit();
201
+ });
202
+ modelsCommand.command("disable <id>").description("Disable a model configuration").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (id, options) => {
203
+ const apiKey = await ensureAuth();
204
+ if (!apiKey) return;
205
+ const client = createCliClient(apiKey);
206
+ if (!isTTY(options)) {
207
+ try {
208
+ await client.patch(`/model-configs/${id}/status`, { enabled: false });
209
+ console.log(JSON.stringify({
210
+ status: "disabled",
211
+ id
212
+ }, null, 2));
213
+ } catch (error) {
214
+ const message = error instanceof Error ? error.message : "Unknown error";
215
+ console.error(chalk.red(`Failed to disable model: ${message}`));
216
+ process.exit(1);
217
+ }
218
+ return;
219
+ }
220
+ const App = () => {
221
+ const [loading, setLoading] = useState(true);
222
+ const [success, setSuccess] = useState(null);
223
+ const [error, setError] = useState(null);
224
+ useEffect(() => {
225
+ client.patch(`/model-configs/${id}/status`, { enabled: false }).then(() => {
226
+ setSuccess(true);
227
+ setLoading(false);
228
+ }).catch((err) => {
229
+ setError(err instanceof Error ? err : new Error(String(err)));
230
+ setSuccess(false);
231
+ setLoading(false);
232
+ });
233
+ }, []);
234
+ return React.createElement(MutationResult, {
235
+ loading,
236
+ loadingLabel: "Disabling model...",
237
+ success,
238
+ successMessage: "Model disabled",
239
+ error
240
+ });
241
+ };
242
+ const { waitUntilExit } = render(React.createElement(App));
243
+ await waitUntilExit();
244
+ });
245
+ modelsCommand.command("default <id>").description("Set a model configuration as default").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (id, options) => {
246
+ const apiKey = await ensureAuth();
247
+ if (!apiKey) return;
248
+ const client = createCliClient(apiKey);
249
+ if (!isTTY(options)) {
250
+ try {
251
+ await client.patch(`/model-configs/${id}/default`, {});
252
+ console.log(JSON.stringify({
253
+ status: "updated",
254
+ id
255
+ }, null, 2));
256
+ } catch (error) {
257
+ const message = error instanceof Error ? error.message : "Unknown error";
258
+ console.error(chalk.red(`Failed to set default model: ${message}`));
259
+ process.exit(1);
260
+ }
261
+ return;
262
+ }
263
+ const App = () => {
264
+ const [loading, setLoading] = useState(true);
265
+ const [success, setSuccess] = useState(null);
266
+ const [error, setError] = useState(null);
267
+ useEffect(() => {
268
+ client.patch(`/model-configs/${id}/default`, {}).then(() => {
269
+ setSuccess(true);
270
+ setLoading(false);
271
+ }).catch((err) => {
272
+ setError(err instanceof Error ? err : new Error(String(err)));
273
+ setSuccess(false);
274
+ setLoading(false);
275
+ });
276
+ }, []);
277
+ return React.createElement(MutationResult, {
278
+ loading,
279
+ loadingLabel: "Setting default model...",
280
+ success,
281
+ successMessage: "Default model updated",
282
+ error
283
+ });
284
+ };
285
+ const { waitUntilExit } = render(React.createElement(App));
286
+ await waitUntilExit();
287
+ });
288
+ modelsCommand.command("usage").description("Show model usage statistics").option("--json", "Output as JSON").action(async () => {
289
+ const apiKey = await ensureAuth();
290
+ if (!apiKey) return;
291
+ console.log(chalk.gray("Fetching model usage..."));
292
+ try {
293
+ const data = await createCliClient(apiKey).get("/model-configs/usage");
294
+ printJson(data);
295
+ } catch (error) {
296
+ const message = error instanceof Error ? error.message : "Unknown error";
297
+ console.error(chalk.red(`Failed to fetch model usage: ${message}`));
298
+ process.exit(1);
299
+ }
300
+ });
301
+ export { modelsCommand };
302
+
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ import { c as mergeZodSchemaAndSemantic, l as validateFPO, o as coerceFullProductObjectForSemanticValidation, s as fullProductObjectSchema, u as validateFPOTemplate } from "./chunk-6uTxgdiY.js";
3
+ import { a as printJson } from "./chunk-CcFCyqbB.js";
4
+ import { Command, Option } from "commander";
5
+ import chalk from "chalk";
6
+ import { readFileSync } from "fs";
7
+ // src/commands/validate-product.ts
8
+ function createValidateProductCommand() {
9
+ return new Command("validate-product").description("Validate a product (FPO) or FPO template locally (no API call)").argument("[input]", "Path to JSON file, raw JSON string (starts with \"{\"), or \"-\" for stdin").option("--template", "Force FPO template validation").option("--fpo", "Force FPO (non-template) validation").addOption(new Option("--json", "Output the full validation result as JSON").default(void 0)).option("-q, --quiet", "Suppress output; rely on exit code only").addHelpText("after", `\nExamples:\n $ runtype validate-product ./my-product.json\n $ runtype validate-product '{"version":"1.0",...}'\n $ cat template.json | runtype validate-product -\n $ runtype validate-product ./tpl.json --template --json\n\nExit codes:\n 0 Valid (no errors)\n 1 Invalid (errors present) or input/IO failure\n`).action(runValidateAction);
10
+ }
11
+ const validateProductCommand = createValidateProductCommand();
12
+ async function runValidateAction(input, _options) {
13
+ const options = this.optsWithGlobals();
14
+ if (options.template && options.fpo) {
15
+ console.error(chalk.red("Error: --template and --fpo are mutually exclusive"));
16
+ process.exit(1);
17
+ }
18
+ let raw;
19
+ try {
20
+ raw = await readInput(input);
21
+ } catch (err) {
22
+ const message = err instanceof Error ? err.message : String(err);
23
+ console.error(chalk.red(`Failed to read input: ${message}`));
24
+ process.exit(1);
25
+ return;
26
+ }
27
+ let parsed;
28
+ try {
29
+ parsed = JSON.parse(raw);
30
+ } catch (err) {
31
+ const message = err instanceof Error ? err.message : String(err);
32
+ console.error(chalk.red(`Invalid JSON: ${message}`));
33
+ process.exit(1);
34
+ return;
35
+ }
36
+ const kind = options.template ? "template" : options.fpo ? "fpo" : detectKind(parsed);
37
+ const result = runValidation(parsed, kind);
38
+ const hasErrors = hasAnyErrors(result);
39
+ result.valid = !hasErrors;
40
+ if (options.quiet) {
41
+ process.exit(hasErrors ? 1 : 0);
42
+ return;
43
+ }
44
+ if (options.json) printJson(result);
45
+ else printHumanResult(kind, result);
46
+ process.exit(hasErrors ? 1 : 0);
47
+ }
48
+ function hasAnyErrors(result) {
49
+ if (result.errors.length > 0) return true;
50
+ if (result.kind === "template") {
51
+ if (result.defaultsValidation.errors.filter((e) => e.code !== "MISSING_TEMPLATE_VARIABLE").length > 0) return true;
52
+ }
53
+ return false;
54
+ }
55
+ async function readInput(input) {
56
+ if (!input || input === "-") return await readStdin();
57
+ const trimmed = input.trim();
58
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) return trimmed;
59
+ return readFileSync(input, "utf-8");
60
+ }
61
+ async function readStdin() {
62
+ if (process.stdin.isTTY) throw new Error("No input provided. Pass a file path, raw JSON, or pipe JSON on stdin.");
63
+ const chunks = [];
64
+ for await (const chunk of process.stdin) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
65
+ const data = Buffer.concat(chunks).toString("utf-8");
66
+ if (data.trim().length === 0) throw new Error("No input provided. Pass a file path, raw JSON, or pipe JSON on stdin.");
67
+ return data;
68
+ }
69
+ function detectKind(body) {
70
+ if (body && typeof body === "object" && !Array.isArray(body)) {
71
+ const obj = body;
72
+ if ("productObject" in obj && "template" in obj) return "template";
73
+ }
74
+ return "fpo";
75
+ }
76
+ function runValidation(body, kind) {
77
+ if (kind === "template") return {
78
+ kind,
79
+ ...validateFPOTemplate(body)
80
+ };
81
+ const parsed = fullProductObjectSchema.safeParse(body);
82
+ if (!parsed.success) return {
83
+ kind,
84
+ ...mergeZodSchemaAndSemantic(parsed, coerceFullProductObjectForSemanticValidation(body), validateFPO)
85
+ };
86
+ return {
87
+ kind,
88
+ ...validateFPO(parsed.data)
89
+ };
90
+ }
91
+ function printHumanResult(kind, result) {
92
+ const label = kind === "template" ? "FPO template" : "FPO";
93
+ const headline = !hasAnyErrors(result) ? chalk.green(`✓ Valid ${label}`) : chalk.red(`✗ Invalid ${label}`);
94
+ console.log(headline);
95
+ console.log(chalk.gray(` ${result.errors.length} error(s), ${result.warnings.length} warning(s), ${result.recommendations.length} recommendation(s)`));
96
+ printIssues("Errors", result.errors, chalk.red);
97
+ printIssues("Warnings", result.warnings, chalk.yellow);
98
+ printIssues("Recommendations", result.recommendations, chalk.blue);
99
+ if (result.kind === "template") {
100
+ const defaults = result.defaultsValidation;
101
+ const structuralErrors = defaults.errors.filter((e) => e.code !== "MISSING_TEMPLATE_VARIABLE");
102
+ const missingVars = defaults.errors.filter((e) => e.code === "MISSING_TEMPLATE_VARIABLE");
103
+ if (structuralErrors.length > 0) {
104
+ console.log();
105
+ console.log(chalk.red(`Defaults validation: template defaults do not produce a valid FPO`));
106
+ printIssues("Default-resolution errors", structuralErrors, chalk.red);
107
+ printIssues("Default-resolution warnings", defaults.warnings, chalk.yellow);
108
+ }
109
+ if (missingVars.length > 0) {
110
+ console.log();
111
+ console.log(chalk.gray(`Template variables needing deploy-time values (${missingVars.length}):`));
112
+ for (const issue of missingVars) console.log(chalk.gray(` • ${issue.path} — ${issue.message}`));
113
+ }
114
+ }
115
+ }
116
+ function printIssues(title, issues, color) {
117
+ if (issues.length === 0) return;
118
+ console.log();
119
+ console.log(color(`${title}:`));
120
+ for (const issue of issues) {
121
+ const pathLabel = issue.path ? chalk.gray(` [${issue.path}]`) : "";
122
+ console.log(` ${color("•")} ${chalk.bold(issue.code)}${pathLabel} ${issue.message}`);
123
+ if (issue.suggestedFix) console.log(` ${chalk.gray("fix:")} ${issue.suggestedFix}`);
124
+ }
125
+ }
126
+ export { validateProductCommand };
127
+
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ import { b as CredentialStore, t as isTTY } from "./index.js";
3
+ import { React, render } from "./chunk-CoAWdSiE.js";
4
+ import { i as getApiUrl, n as ApiKeyValidationUnavailableError, t as ApiKeyManager } from "./chunk-BW2fg46T.js";
5
+ import { Command } from "commander";
6
+ // src/commands/init.ts
7
+ const initCommand = new Command("init").description("Set up Runtype CLI with guided onboarding").option("--api-url <url>", "Custom API URL").option("--api-key <key>", "API key for non-interactive setup").option("--name <name>", "Product name for non-interactive setup").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
8
+ if (!isTTY(options)) {
9
+ if (!options.apiKey) {
10
+ console.error(JSON.stringify({
11
+ status: "error",
12
+ error: "Missing --api-key flag for non-interactive mode"
13
+ }, null, 2));
14
+ process.exit(1);
15
+ }
16
+ try {
17
+ const apiKeyManager = new ApiKeyManager();
18
+ if (!await apiKeyManager.validateApiKey(options.apiKey, options.apiUrl)) {
19
+ console.error(JSON.stringify({
20
+ status: "error",
21
+ error: "Invalid API key"
22
+ }, null, 2));
23
+ process.exit(1);
24
+ }
25
+ const userData = await apiKeyManager.getCurrentUser(options.apiKey, options.apiUrl);
26
+ await new CredentialStore().saveCredentials({
27
+ apiKey: options.apiKey,
28
+ userId: userData.userId,
29
+ orgId: userData.orgId ?? void 0,
30
+ apiUrl: options.apiUrl || getApiUrl()
31
+ });
32
+ console.log(JSON.stringify({
33
+ status: "authenticated",
34
+ userId: userData.userId,
35
+ orgId: userData.orgId
36
+ }, null, 2));
37
+ } catch (error) {
38
+ if (error instanceof ApiKeyValidationUnavailableError) {
39
+ console.error(JSON.stringify({
40
+ status: "error",
41
+ code: "api_unreachable",
42
+ error: error.message,
43
+ next: "If egress is restricted, allowlist api.runtype.com (staging: api.runtype-staging.com), then re-run: runtype init --api-key <key>"
44
+ }, null, 2));
45
+ process.exit(1);
46
+ }
47
+ const message = error instanceof Error ? error.message : "Authentication failed";
48
+ console.error(JSON.stringify({
49
+ status: "error",
50
+ error: message
51
+ }, null, 2));
52
+ process.exit(1);
53
+ }
54
+ return;
55
+ }
56
+ if (options.apiKey) try {
57
+ const apiKeyManager = new ApiKeyManager();
58
+ if (!await apiKeyManager.validateApiKey(options.apiKey, options.apiUrl)) {
59
+ console.error("Invalid API key");
60
+ process.exit(1);
61
+ }
62
+ const userData = await apiKeyManager.getCurrentUser(options.apiKey, options.apiUrl);
63
+ await new CredentialStore().saveCredentials({
64
+ apiKey: options.apiKey,
65
+ userId: userData.userId,
66
+ orgId: userData.orgId ?? void 0,
67
+ apiUrl: options.apiUrl || getApiUrl()
68
+ });
69
+ } catch (error) {
70
+ const message = error instanceof Error ? error.message : "Authentication failed";
71
+ console.error(message);
72
+ process.exit(1);
73
+ }
74
+ const { InitApp } = await import("./chunk-DwDWqSxM.js");
75
+ const { waitUntilExit } = render(React.createElement(InitApp, {
76
+ apiUrl: options.apiUrl,
77
+ onComplete: () => {}
78
+ }), { exitOnCtrlC: false });
79
+ await waitUntilExit();
80
+ });
81
+ export { initCommand };
82
+