@supercheck/cli 0.1.1-rc.3 → 0.1.2

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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  confirmPrompt
4
- } from "../chunk-W54DX2I7.js";
4
+ } from "../chunk-KIAFFV4V.js";
5
5
 
6
6
  // src/bin/supercheck.ts
7
7
  import { Command as Command21 } from "commander";
@@ -222,7 +222,7 @@ function requireTriggerKey() {
222
222
  }
223
223
 
224
224
  // src/version.ts
225
- var CLI_VERSION = true ? "0.1.1-rc.3" : "0.0.0-dev";
225
+ var CLI_VERSION = true ? "0.1.2" : "0.0.0-dev";
226
226
 
227
227
  // src/utils/proxy.ts
228
228
  import { ProxyAgent } from "undici";
@@ -571,12 +571,13 @@ function buildLocalResources(config, cwd) {
571
571
  const resources = [];
572
572
  if (config.jobs) {
573
573
  for (const job of config.jobs) {
574
+ const { status: _status, ...jobWithoutStatus } = job;
574
575
  resources.push({
575
576
  id: job.id,
576
577
  type: "job",
577
578
  name: job.name,
578
579
  definition: {
579
- ...job,
580
+ ...jobWithoutStatus,
580
581
  tests: normalizeJobTestsToIds(job.tests)
581
582
  }
582
583
  });
@@ -1064,6 +1065,17 @@ function maybeFormatDate(value, key) {
1064
1065
  function formatDate(date) {
1065
1066
  return date.toISOString().replace("T", " ").replace("Z", "");
1066
1067
  }
1068
+ function outputPagination(pagination) {
1069
+ if (!pagination) return;
1070
+ if (currentFormat === "json") return;
1071
+ const page = pagination.page ?? pagination.currentPage ?? 1;
1072
+ const total = pagination.total ?? pagination.totalCount ?? 0;
1073
+ if (total === 0) return;
1074
+ logger.info(
1075
+ `
1076
+ Page ${page}/${pagination.totalPages} (${total} total)`
1077
+ );
1078
+ }
1067
1079
  function summarizeArray(_key, items) {
1068
1080
  if (items.length === 0) return pc2.dim("(none)");
1069
1081
  if (typeof items[0] !== "object" || items[0] === null) {
@@ -1166,123 +1178,6 @@ async function withSpinner(text, fn, options) {
1166
1178
  }
1167
1179
  }
1168
1180
 
1169
- // src/commands/login.ts
1170
- var loginCommand = new Command("login").description("Authenticate with Supercheck").option("--token <token>", "Provide a CLI token directly (for CI/CD)").option("--url <url>", "Supercheck API URL (for self-hosted instances)").action(async (options) => {
1171
- if (options.token) {
1172
- const token = options.token.trim();
1173
- if (!validateTokenFormat(token)) {
1174
- throw new CLIError(
1175
- "Invalid token. `supercheck login` requires a CLI token (sck_live_* or sck_test_*). Trigger keys (sck_trigger_* / job_*) are only for `supercheck job trigger` via SUPERCHECK_TRIGGER_KEY.",
1176
- 2 /* AuthError */
1177
- );
1178
- }
1179
- const client = getApiClient({
1180
- baseUrl: options.url,
1181
- token
1182
- });
1183
- try {
1184
- await withSpinner(
1185
- "Verifying token...",
1186
- () => client.get("/api/cli-tokens"),
1187
- { successText: "Token verified" }
1188
- );
1189
- } catch {
1190
- throw new CLIError(
1191
- "Token verification failed. Please check your token and try again.",
1192
- 2 /* AuthError */
1193
- );
1194
- }
1195
- setToken(token);
1196
- if (options.url) {
1197
- setBaseUrl(options.url);
1198
- }
1199
- const tokenPreview = safeTokenPreview(token);
1200
- logger.success("Authentication successful");
1201
- logger.info(` Token: ${tokenPreview}`);
1202
- if (options.url) {
1203
- logger.info(` API URL: ${options.url}`);
1204
- }
1205
- return;
1206
- }
1207
- logger.newline();
1208
- logger.info("To authenticate, create a CLI token in the Dashboard:");
1209
- logger.info(" Dashboard \u2192 Project Settings \u2192 CLI Tokens \u2192 Create Token");
1210
- logger.newline();
1211
- if (options.url) {
1212
- logger.info(`Then run: supercheck login --token <your-token> --url ${options.url}`);
1213
- } else {
1214
- logger.info("Then run: supercheck login --token <your-token>");
1215
- }
1216
- logger.newline();
1217
- logger.warn("Browser-based OAuth login is not yet implemented.");
1218
- });
1219
- var logoutCommand = new Command("logout").description("Remove stored authentication credentials").action(() => {
1220
- clearAuth();
1221
- logger.success("Logged out successfully. Stored credentials removed.");
1222
- });
1223
- var whoamiCommand = new Command("whoami").description("Show current authentication context").action(async () => {
1224
- const token = getToken();
1225
- if (!token) {
1226
- throw new CLIError(
1227
- "Not authenticated. Run `supercheck login` to authenticate.",
1228
- 2 /* AuthError */
1229
- );
1230
- }
1231
- const baseUrl = getStoredBaseUrl();
1232
- const client = getApiClient({ token, baseUrl: baseUrl ?? void 0 });
1233
- try {
1234
- const data = await withSpinner(
1235
- "Fetching context...",
1236
- async () => {
1237
- const { data: data2 } = await client.get("/api/cli-tokens");
1238
- return data2;
1239
- },
1240
- { successText: "Context loaded" }
1241
- );
1242
- const tokenPreview = safeTokenPreview(token);
1243
- const activeToken = data.tokens?.find((t) => t.start && token.startsWith(t.start.replace(/\.+$/, "")));
1244
- if (getOutputFormat() === "json") {
1245
- const jsonData = {
1246
- user: activeToken?.createdByName ?? null,
1247
- tokenName: activeToken?.name ?? null,
1248
- tokenPreview,
1249
- apiUrl: baseUrl ?? "https://app.supercheck.io",
1250
- expiresAt: activeToken?.expiresAt ?? null,
1251
- lastUsed: activeToken?.lastRequest ?? null
1252
- };
1253
- logger.output(JSON.stringify(jsonData, null, 2));
1254
- return;
1255
- }
1256
- logger.newline();
1257
- logger.header("Current Context");
1258
- logger.newline();
1259
- if (activeToken?.createdByName && activeToken.createdByName !== "-") {
1260
- logger.info(` User: ${activeToken.createdByName}`);
1261
- }
1262
- if (activeToken?.name) {
1263
- logger.info(` Token: ${activeToken.name} (${tokenPreview})`);
1264
- } else {
1265
- logger.info(` Token: ${tokenPreview}`);
1266
- }
1267
- logger.info(` API URL: ${baseUrl ?? "https://app.supercheck.io"}`);
1268
- if (activeToken?.expiresAt) {
1269
- logger.info(` Expires: ${activeToken.expiresAt}`);
1270
- }
1271
- if (activeToken?.lastRequest) {
1272
- logger.info(` Last used: ${activeToken.lastRequest}`);
1273
- }
1274
- logger.newline();
1275
- } catch {
1276
- throw new CLIError(
1277
- "Failed to verify authentication. Your token may be expired or invalid.",
1278
- 2 /* AuthError */
1279
- );
1280
- }
1281
- });
1282
-
1283
- // src/commands/health.ts
1284
- import { Command as Command2 } from "commander";
1285
-
1286
1181
  // src/config/loader.ts
1287
1182
  import { createJiti } from "jiti";
1288
1183
  import { deepmerge } from "deepmerge-ts";
@@ -1377,6 +1272,7 @@ var statusPageDefinitionSchema = z.object({
1377
1272
  description: z.string().optional(),
1378
1273
  headline: z.string().optional(),
1379
1274
  supportUrl: z.string().optional(),
1275
+ language: z.string().min(2).max(10).optional(),
1380
1276
  components: z.array(statusPageComponentDefinitionSchema).optional(),
1381
1277
  branding: z.object({
1382
1278
  bodyBackgroundColor: z.string().optional(),
@@ -1544,7 +1440,157 @@ async function tryLoadConfig(options = {}) {
1544
1440
  }
1545
1441
  }
1546
1442
 
1443
+ // src/api/authenticated-client.ts
1444
+ var resolvedConfigBaseUrl = null;
1445
+ function getResolvedConfigBaseUrl() {
1446
+ return resolvedConfigBaseUrl;
1447
+ }
1448
+ function setResolvedConfigBaseUrl(baseUrl) {
1449
+ resolvedConfigBaseUrl = baseUrl;
1450
+ }
1451
+ async function resolveAndSetConfigBaseUrl(options = {}) {
1452
+ const resolved = await resolveConfigBaseUrl(options);
1453
+ setResolvedConfigBaseUrl(resolved);
1454
+ return resolved;
1455
+ }
1456
+ async function resolveConfigBaseUrl(options = {}) {
1457
+ if (options.configPath) {
1458
+ const result = await loadConfig(options);
1459
+ return result.config.api?.baseUrl ?? null;
1460
+ }
1461
+ try {
1462
+ const result = await tryLoadConfig(options);
1463
+ return result?.config?.api?.baseUrl ?? null;
1464
+ } catch (err) {
1465
+ logger.debug(
1466
+ `Skipping config base URL fallback: ${err instanceof Error ? err.message : String(err)}`
1467
+ );
1468
+ return null;
1469
+ }
1470
+ }
1471
+ function createAuthenticatedClient(configBaseUrl) {
1472
+ const token = requireAuth();
1473
+ const storedBaseUrl = getStoredBaseUrl();
1474
+ const baseUrl = storedBaseUrl ?? configBaseUrl ?? resolvedConfigBaseUrl;
1475
+ return getApiClient({ token, baseUrl: baseUrl ?? void 0 });
1476
+ }
1477
+
1478
+ // src/commands/login.ts
1479
+ var loginCommand = new Command("login").description("Authenticate with Supercheck").option("--token <token>", "Provide a CLI token directly (for CI/CD)").option("--url <url>", "Supercheck API URL (for self-hosted instances)").action(async (options) => {
1480
+ if (options.token) {
1481
+ const token = options.token.trim();
1482
+ if (!validateTokenFormat(token)) {
1483
+ throw new CLIError(
1484
+ "Invalid token. `supercheck login` requires a CLI token (sck_live_* or sck_test_*). Trigger keys (sck_trigger_* / job_*) are only for `supercheck job trigger` via SUPERCHECK_TRIGGER_KEY.",
1485
+ 2 /* AuthError */
1486
+ );
1487
+ }
1488
+ const client = getApiClient({
1489
+ baseUrl: options.url,
1490
+ token
1491
+ });
1492
+ try {
1493
+ await withSpinner(
1494
+ "Verifying token...",
1495
+ () => client.get("/api/cli-tokens"),
1496
+ { successText: "Token verified" }
1497
+ );
1498
+ } catch {
1499
+ throw new CLIError(
1500
+ "Token verification failed. Please check your token and try again.",
1501
+ 2 /* AuthError */
1502
+ );
1503
+ }
1504
+ setToken(token);
1505
+ if (options.url) {
1506
+ setBaseUrl(options.url);
1507
+ }
1508
+ const tokenPreview = safeTokenPreview(token);
1509
+ logger.success("Authentication successful");
1510
+ logger.info(` Token: ${tokenPreview}`);
1511
+ if (options.url) {
1512
+ logger.info(` API URL: ${options.url}`);
1513
+ }
1514
+ return;
1515
+ }
1516
+ logger.newline();
1517
+ logger.info("To authenticate, create a CLI token in the Dashboard:");
1518
+ logger.info(" Dashboard \u2192 Project Settings \u2192 CLI Tokens \u2192 Create Token");
1519
+ logger.newline();
1520
+ if (options.url) {
1521
+ logger.info(`Then run: supercheck login --token <your-token> --url ${options.url}`);
1522
+ } else {
1523
+ logger.info("Then run: supercheck login --token <your-token>");
1524
+ }
1525
+ logger.newline();
1526
+ logger.warn("Browser-based OAuth login is not yet implemented.");
1527
+ });
1528
+ var logoutCommand = new Command("logout").description("Remove stored authentication credentials").action(() => {
1529
+ clearAuth();
1530
+ logger.success("Logged out successfully. Stored credentials removed.");
1531
+ });
1532
+ var whoamiCommand = new Command("whoami").description("Show current authentication context").action(async () => {
1533
+ const token = getToken();
1534
+ if (!token) {
1535
+ throw new CLIError(
1536
+ "Not authenticated. Run `supercheck login` to authenticate.",
1537
+ 2 /* AuthError */
1538
+ );
1539
+ }
1540
+ const baseUrl = getStoredBaseUrl() ?? getResolvedConfigBaseUrl();
1541
+ const client = getApiClient({ token, baseUrl: baseUrl ?? void 0 });
1542
+ try {
1543
+ const data = await withSpinner(
1544
+ "Fetching context...",
1545
+ async () => {
1546
+ const { data: data2 } = await client.get("/api/cli-tokens");
1547
+ return data2;
1548
+ },
1549
+ { successText: "Context loaded" }
1550
+ );
1551
+ const tokenPreview = safeTokenPreview(token);
1552
+ const activeToken = data.tokens?.find((t) => t.start && token.startsWith(t.start.replace(/\.+$/, "")));
1553
+ if (getOutputFormat() === "json") {
1554
+ const jsonData = {
1555
+ user: activeToken?.createdByName ?? null,
1556
+ tokenName: activeToken?.name ?? null,
1557
+ tokenPreview,
1558
+ apiUrl: baseUrl ?? "https://app.supercheck.io",
1559
+ expiresAt: activeToken?.expiresAt ?? null,
1560
+ lastUsed: activeToken?.lastRequest ?? null
1561
+ };
1562
+ logger.output(JSON.stringify(jsonData, null, 2));
1563
+ return;
1564
+ }
1565
+ logger.newline();
1566
+ logger.header("Current Context");
1567
+ logger.newline();
1568
+ if (activeToken?.createdByName && activeToken.createdByName !== "-") {
1569
+ logger.info(` User: ${activeToken.createdByName}`);
1570
+ }
1571
+ if (activeToken?.name) {
1572
+ logger.info(` Token: ${activeToken.name} (${tokenPreview})`);
1573
+ } else {
1574
+ logger.info(` Token: ${tokenPreview}`);
1575
+ }
1576
+ logger.info(` API URL: ${baseUrl ?? "https://app.supercheck.io"}`);
1577
+ if (activeToken?.expiresAt) {
1578
+ logger.info(` Expires: ${activeToken.expiresAt}`);
1579
+ }
1580
+ if (activeToken?.lastRequest) {
1581
+ logger.info(` Last used: ${activeToken.lastRequest}`);
1582
+ }
1583
+ logger.newline();
1584
+ } catch {
1585
+ throw new CLIError(
1586
+ "Failed to verify authentication. Your token may be expired or invalid.",
1587
+ 2 /* AuthError */
1588
+ );
1589
+ }
1590
+ });
1591
+
1547
1592
  // src/commands/health.ts
1593
+ import { Command as Command2 } from "commander";
1548
1594
  var healthCommand = new Command2("health").description("Check Supercheck API health").option("--url <url>", "Supercheck API URL").action(async (options) => {
1549
1595
  const configResult = await tryLoadConfig();
1550
1596
  const baseUrl = options.url ?? getStoredBaseUrl() ?? configResult?.config.api?.baseUrl ?? "https://app.supercheck.io";
@@ -1576,7 +1622,9 @@ var healthCommand = new Command2("health").description("Check Supercheck API hea
1576
1622
  ]
1577
1623
  });
1578
1624
  if (data.status === "ok") {
1579
- logger.success(`API is healthy at ${baseUrl}`);
1625
+ if (getOutputFormat() !== "json") {
1626
+ logger.success(`API is healthy at ${baseUrl}`);
1627
+ }
1580
1628
  } else {
1581
1629
  throw new CLIError(`API reports degraded status at ${baseUrl}`, 4 /* ApiError */);
1582
1630
  }
@@ -1590,9 +1638,7 @@ var healthCommand = new Command2("health").description("Check Supercheck API hea
1590
1638
  }
1591
1639
  });
1592
1640
  var locationsCommand = new Command2("locations").description("List available execution locations").action(async () => {
1593
- const configResult = await tryLoadConfig();
1594
- const baseUrl = getStoredBaseUrl() ?? configResult?.config.api?.baseUrl ?? "https://app.supercheck.io";
1595
- const client = getApiClient({ baseUrl });
1641
+ const client = createAuthenticatedClient();
1596
1642
  const locations = await withSpinner(
1597
1643
  "Fetching execution locations...",
1598
1644
  async () => {
@@ -1706,13 +1752,14 @@ Error: ${message}`,
1706
1752
  }
1707
1753
 
1708
1754
  // src/utils/deps.ts
1709
- import { execSync } from "child_process";
1710
- import { existsSync as existsSync3 } from "fs";
1711
- import { resolve as resolve4 } from "path";
1755
+ import { execFileSync, execSync } from "child_process";
1756
+ import { existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
1757
+ import { homedir } from "os";
1758
+ import { resolve as resolve4, join } from "path";
1712
1759
  import pc3 from "picocolors";
1713
1760
  function isCommandAvailable(command) {
1714
1761
  try {
1715
- const output2 = execSync(`${command} --version`, {
1762
+ const output2 = execFileSync(command, ["--version"], {
1716
1763
  stdio: "pipe",
1717
1764
  timeout: 1e4,
1718
1765
  env: { ...process.env }
@@ -1723,6 +1770,54 @@ function isCommandAvailable(command) {
1723
1770
  return { available: false };
1724
1771
  }
1725
1772
  }
1773
+ function getPlaywrightBrowserDirs(cwd) {
1774
+ const configuredPath = process.env.PLAYWRIGHT_BROWSERS_PATH?.trim();
1775
+ if (configuredPath) {
1776
+ if (configuredPath === "0") {
1777
+ return [
1778
+ resolve4(cwd, "node_modules", "playwright-core", ".local-browsers"),
1779
+ resolve4(cwd, "node_modules", "playwright", ".local-browsers")
1780
+ ];
1781
+ }
1782
+ return [resolve4(configuredPath)];
1783
+ }
1784
+ const home = homedir();
1785
+ const dirs = [
1786
+ resolve4(cwd, "node_modules", "playwright-core", ".local-browsers"),
1787
+ resolve4(cwd, "node_modules", "playwright", ".local-browsers")
1788
+ ];
1789
+ switch (process.platform) {
1790
+ case "darwin":
1791
+ dirs.push(join(home, "Library", "Caches", "ms-playwright"));
1792
+ break;
1793
+ case "linux":
1794
+ dirs.push(join(home, ".cache", "ms-playwright"));
1795
+ break;
1796
+ case "win32": {
1797
+ const base = process.env.LOCALAPPDATA ? resolve4(process.env.LOCALAPPDATA) : join(home, "AppData", "Local");
1798
+ dirs.push(join(base, "ms-playwright"));
1799
+ break;
1800
+ }
1801
+ }
1802
+ return dirs;
1803
+ }
1804
+ function arePlaywrightBrowsersInstalled(cwd) {
1805
+ for (const dir of getPlaywrightBrowserDirs(cwd)) {
1806
+ if (!existsSync3(dir)) continue;
1807
+ try {
1808
+ const entries = readdirSync2(dir, { withFileTypes: true });
1809
+ const hasChromium = entries.some((entry) => {
1810
+ if (!entry.isDirectory()) return false;
1811
+ return entry.name.startsWith("chromium-") || entry.name.startsWith("chromium_headless_shell-");
1812
+ });
1813
+ if (hasChromium) {
1814
+ return true;
1815
+ }
1816
+ } catch {
1817
+ }
1818
+ }
1819
+ return false;
1820
+ }
1726
1821
  function isPlaywrightPackageInstalled(cwd) {
1727
1822
  try {
1728
1823
  const pkgPath = resolve4(cwd, "node_modules", "@playwright", "test", "package.json");
@@ -1777,12 +1872,11 @@ function checkAllDependencies(cwd) {
1777
1872
  installHint: "npm install --save-dev @playwright/test"
1778
1873
  });
1779
1874
  if (pwPkg) {
1780
- const pwCheck = isCommandAvailable("npx playwright --version");
1875
+ const browsersInstalled = arePlaywrightBrowsersInstalled(cwd);
1781
1876
  deps.push({
1782
1877
  name: "Playwright browsers",
1783
- installed: pwCheck.available,
1784
- version: pwCheck.version,
1785
- detail: pwCheck.available ? "browsers available" : "run: npx playwright install chromium",
1878
+ installed: browsersInstalled,
1879
+ detail: browsersInstalled ? "chromium browser available" : "run: npx playwright install chromium",
1786
1880
  required: true,
1787
1881
  installHint: "npx playwright install chromium"
1788
1882
  });
@@ -1849,6 +1943,12 @@ function ensureDependenciesForTestType(cwd, testType) {
1849
1943
  "@playwright/test is not installed.\n Install it with: npm install --save-dev @playwright/test\n Then install browsers: npx playwright install chromium"
1850
1944
  );
1851
1945
  }
1946
+ if (!arePlaywrightBrowsersInstalled(cwd)) {
1947
+ throw new DependencyError(
1948
+ "playwright-browsers",
1949
+ "Playwright browsers are not installed.\n Install them with: npx playwright install chromium"
1950
+ );
1951
+ }
1852
1952
  }
1853
1953
  if (testType === "k6") {
1854
1954
  const k6 = isK6Installed();
@@ -1983,6 +2083,13 @@ supercheck.config.local.mjs
1983
2083
  `;
1984
2084
  var initCommand = new Command4("init").description("Initialize a new Supercheck project with config and example tests").option("--force", "Overwrite existing config file").option("--skip-install", "Skip automatic dependency installation").option("--skip-examples", "Skip creating example test files").option("--pm <manager>", "Package manager to use (npm, yarn, pnpm, bun)").action(async (options) => {
1985
2085
  const cwd = process.cwd();
2086
+ const requestedPm = options.pm?.trim().toLowerCase();
2087
+ if (requestedPm && !["npm", "yarn", "pnpm", "bun"].includes(requestedPm)) {
2088
+ throw new CLIError(
2089
+ `Invalid value for --pm: "${options.pm}". Expected one of: npm, yarn, pnpm, bun.`,
2090
+ 3 /* ConfigError */
2091
+ );
2092
+ }
1986
2093
  const configPath = resolve5(cwd, "supercheck.config.ts");
1987
2094
  if (existsSync4(configPath) && !options.force) {
1988
2095
  throw new CLIError(
@@ -2037,7 +2144,7 @@ var initCommand = new Command4("init").description("Initialize a new Supercheck
2037
2144
  logger.success("Updated .gitignore with Supercheck entries");
2038
2145
  }
2039
2146
  }
2040
- const pm = options.pm ?? detectPackageManager(cwd);
2147
+ const pm = requestedPm ?? detectPackageManager(cwd);
2041
2148
  logger.debug(`Detected package manager: ${pm}`);
2042
2149
  const createdPkg = ensurePackageJson(cwd);
2043
2150
  if (createdPkg) {
@@ -2088,14 +2195,6 @@ import { Command as Command5 } from "commander";
2088
2195
  import { readFileSync as readFileSync3 } from "fs";
2089
2196
  import { resolve as resolve6 } from "path";
2090
2197
 
2091
- // src/api/authenticated-client.ts
2092
- function createAuthenticatedClient(configBaseUrl) {
2093
- const token = requireAuth();
2094
- const storedBaseUrl = getStoredBaseUrl();
2095
- const baseUrl = storedBaseUrl ?? configBaseUrl;
2096
- return getApiClient({ token, baseUrl: baseUrl ?? void 0 });
2097
- }
2098
-
2099
2198
  // src/utils/number.ts
2100
2199
  function parseIntStrict(value, name, opts) {
2101
2200
  const parsed = parseInt(value, 10);
@@ -2119,6 +2218,15 @@ function parseIntStrict(value, name, opts) {
2119
2218
  }
2120
2219
  return parsed;
2121
2220
  }
2221
+ function parseBooleanStrict(value, name) {
2222
+ const normalized = value.trim().toLowerCase();
2223
+ if (normalized === "true") return true;
2224
+ if (normalized === "false") return false;
2225
+ throw new CLIError(
2226
+ `Invalid value for ${name}: "${value}" is not a valid boolean. Expected "true" or "false".`,
2227
+ 3 /* ConfigError */
2228
+ );
2229
+ }
2122
2230
 
2123
2231
  // src/utils/validation.ts
2124
2232
  var K6_IMPORT_PATTERN = /import\s+.*\s+from\s+['"]k6(?:\/[^'"]*)?['"]/;
@@ -2213,10 +2321,10 @@ function runCommand(command, args, cwd) {
2213
2321
 
2214
2322
  // src/utils/playwright.ts
2215
2323
  import { mkdtempSync, writeFileSync as writeFileSync3, rmSync } from "fs";
2216
- import { join } from "path";
2324
+ import { join as join2 } from "path";
2217
2325
  function createTempPlaywrightConfig(cwd, testMatch) {
2218
- const dir = mkdtempSync(join(cwd, ".supercheck-playwright-"));
2219
- const filePath = join(dir, "playwright.supercheck.config.mjs");
2326
+ const dir = mkdtempSync(join2(cwd, ".supercheck-playwright-"));
2327
+ const filePath = join2(dir, "playwright.supercheck.config.mjs");
2220
2328
  const match = testMatch ?? "_supercheck_/playwright/**/*.pw.ts";
2221
2329
  const contents = [
2222
2330
  "import { defineConfig } from '@playwright/test'",
@@ -2237,6 +2345,7 @@ function createTempPlaywrightConfig(cwd, testMatch) {
2237
2345
  }
2238
2346
 
2239
2347
  // src/commands/jobs.ts
2348
+ var VALID_JOB_TYPES = ["playwright", "k6"];
2240
2349
  var jobCommand = new Command5("job").description("Manage jobs");
2241
2350
  function inferLocalTestType(filePath) {
2242
2351
  return filePath.endsWith(".k6.ts") || filePath.endsWith(".k6.js") ? "k6" : "playwright";
@@ -2344,12 +2453,7 @@ jobCommand.command("list").description("List all jobs").option("--page <page>",
2344
2453
  { key: "createdAt", header: "Created" }
2345
2454
  ]
2346
2455
  });
2347
- if (data.pagination) {
2348
- logger.info(
2349
- `
2350
- Page ${data.pagination.page}/${data.pagination.totalPages} (${data.pagination.total} total)`
2351
- );
2352
- }
2456
+ outputPagination(data.pagination);
2353
2457
  });
2354
2458
  var keysCommand = jobCommand.command("keys").description("Manage job trigger keys");
2355
2459
  keysCommand.argument("<jobId>", "Job ID").action(async (jobId) => {
@@ -2413,10 +2517,18 @@ jobCommand.command("create").description("Create a new job").requiredOption("--n
2413
2517
  const body = {
2414
2518
  name: options.name,
2415
2519
  description: options.description,
2416
- config: {},
2417
2520
  tests: tests.map((id) => ({ id }))
2418
2521
  };
2419
- if (options.type) body.jobType = options.type;
2522
+ if (options.type) {
2523
+ const type = options.type.trim().toLowerCase();
2524
+ if (!VALID_JOB_TYPES.includes(type)) {
2525
+ throw new CLIError(
2526
+ `Invalid value for --type: "${options.type}". Expected one of: ${VALID_JOB_TYPES.join(", ")}.`,
2527
+ 3 /* ConfigError */
2528
+ );
2529
+ }
2530
+ body.jobType = type;
2531
+ }
2420
2532
  if (options.schedule) body.cronSchedule = options.schedule;
2421
2533
  if (options.dryRun) {
2422
2534
  logger.header("Dry run \u2014 job create payload:");
@@ -2432,15 +2544,17 @@ jobCommand.command("create").description("Create a new job").requiredOption("--n
2432
2544
  logger.success(`Job "${options.name}" created (${jobId ?? "unknown"})`);
2433
2545
  outputDetail(job);
2434
2546
  });
2435
- jobCommand.command("update <id>").description("Update job configuration").option("--name <name>", "Job name").option("--description <description>", "Job description").option("--schedule <cron>", "Cron schedule expression").option("--status <status>", "Job status (active, paused)").option("--dry-run", "Show what would be sent without updating").action(async (id, options) => {
2547
+ jobCommand.command("update <id>").description("Update job configuration").option("--name <name>", "Job name").option("--description <description>", "Job description").option("--schedule <cron>", "Cron schedule expression").option("--status <status>", "[deprecated] Job status is runtime state and cannot be updated via the CLI").option("--dry-run", "Show what would be sent without updating").action(async (id, options) => {
2436
2548
  const client = createAuthenticatedClient();
2437
2549
  const body = {};
2438
2550
  if (options.name !== void 0) body.name = options.name;
2439
2551
  if (options.description !== void 0) body.description = options.description;
2440
2552
  if (options.schedule !== void 0) body.cronSchedule = options.schedule;
2441
- if (options.status !== void 0) body.status = options.status;
2553
+ if (options.status !== void 0) {
2554
+ logger.warn("Ignoring --status: job status is runtime state and is not editable via the current API.");
2555
+ }
2442
2556
  if (Object.keys(body).length === 0) {
2443
- logger.warn("No fields to update. Use --name, --description, --schedule, or --status.");
2557
+ logger.warn("No fields to update. Use --name, --description, or --schedule.");
2444
2558
  return;
2445
2559
  }
2446
2560
  if (options.dryRun) {
@@ -2457,7 +2571,7 @@ jobCommand.command("update <id>").description("Update job configuration").option
2457
2571
  });
2458
2572
  jobCommand.command("delete <id>").description("Delete a job").option("--force", "Skip confirmation").action(async (id, options) => {
2459
2573
  if (!options.force) {
2460
- const { confirmPrompt: confirmPrompt2 } = await import("../prompt-BPDPYRS7.js");
2574
+ const { confirmPrompt: confirmPrompt2 } = await import("../prompt-L3F2UJRB.js");
2461
2575
  const confirmed = await confirmPrompt2(`Delete job ${id}?`, { default: false });
2462
2576
  if (!confirmed) {
2463
2577
  logger.info("Aborted");
@@ -2530,7 +2644,7 @@ jobCommand.command("run").description("Run a job immediately").requiredOption("-
2530
2644
  jobCommand.command("trigger <id>").description("Trigger a job run").option("--wait", "Wait for the run to complete").option("--timeout <seconds>", "Maximum wait time in seconds", "300").action(async (id, options) => {
2531
2645
  const triggerClient = new ApiClient({
2532
2646
  token: requireTriggerKey(),
2533
- baseUrl: getStoredBaseUrl() ?? void 0
2647
+ baseUrl: getStoredBaseUrl() ?? getResolvedConfigBaseUrl() ?? void 0
2534
2648
  });
2535
2649
  const { data } = await withSpinner(
2536
2650
  "Triggering job",
@@ -2611,12 +2725,7 @@ runCommand2.command("list").description("List runs").option("--page <page>", "Pa
2611
2725
  { key: "startedAt", header: "Started" }
2612
2726
  ]
2613
2727
  });
2614
- if (data.pagination) {
2615
- logger.info(
2616
- `
2617
- Page ${data.pagination.page}/${data.pagination.totalPages} (${data.pagination.total} total)`
2618
- );
2619
- }
2728
+ outputPagination(data.pagination);
2620
2729
  });
2621
2730
  runCommand2.command("get <id>").description("Get run details").action(async (id) => {
2622
2731
  const client = createAuthenticatedClient();
@@ -2645,7 +2754,7 @@ runCommand2.command("permissions <id>").description("Get run access permissions"
2645
2754
  });
2646
2755
  runCommand2.command("stream <id>").description("Stream live console output from a run").option("--idle-timeout <seconds>", "Abort if no data received within this period", "60").action(async (id, options) => {
2647
2756
  const token = requireAuth();
2648
- const baseUrl = getStoredBaseUrl() ?? "https://app.supercheck.io";
2757
+ const baseUrl = getStoredBaseUrl() ?? getResolvedConfigBaseUrl() ?? "https://app.supercheck.io";
2649
2758
  const idleTimeoutMs = Math.max(Number(options.idleTimeout) || 60, 10) * 1e3;
2650
2759
  const url = `${baseUrl}/api/runs/${id}/stream`;
2651
2760
  const parsedUrl = new URL(url);
@@ -2766,7 +2875,17 @@ import { readFileSync as readFileSync4 } from "fs";
2766
2875
  import { resolve as resolve7 } from "path";
2767
2876
  import { Buffer as Buffer2 } from "buffer";
2768
2877
  function normalizeTestType(input) {
2769
- return normalizeTestTypeForApi(input) ?? input.trim().toLowerCase();
2878
+ return requireApiTestType(input, "--type");
2879
+ }
2880
+ function requireApiTestType(input, optionName) {
2881
+ const normalized = normalizeTestTypeForApi(input);
2882
+ if (!normalized) {
2883
+ throw new CLIError(
2884
+ `Invalid value for ${optionName}: "${input}". Expected one of: browser, performance, api, database, custom.`,
2885
+ 3 /* ConfigError */
2886
+ );
2887
+ }
2888
+ return normalized;
2770
2889
  }
2771
2890
  function inferTestType(filename) {
2772
2891
  if (filename.endsWith(".k6.ts") || filename.endsWith(".k6.js")) return "k6";
@@ -2781,7 +2900,7 @@ function collectLocalTests(cwd, patterns, options) {
2781
2900
  if (options.file) {
2782
2901
  const filePath = resolve7(cwd, options.file);
2783
2902
  const type = inferTestType(filePath) ?? (options.type === "k6" ? "k6" : "playwright");
2784
- const validationType = options.type ? normalizeTestTypeForApi(options.type) : normalizeTestTypeForApi(type);
2903
+ const validationType = options.type ? requireApiTestType(options.type, "--type") : normalizeTestTypeForApi(type);
2785
2904
  const script = readFileSync4(filePath, "utf-8");
2786
2905
  return [{
2787
2906
  path: filePath,
@@ -2791,12 +2910,12 @@ function collectLocalTests(cwd, patterns, options) {
2791
2910
  validationType
2792
2911
  }];
2793
2912
  }
2794
- const typeFilter = options.type?.toLowerCase();
2795
- const filterType = typeFilter && ["api", "database", "custom", "browser", "playwright"].includes(typeFilter) ? "playwright" : typeFilter === "performance" || typeFilter === "load" ? "k6" : typeFilter;
2913
+ const requestedType = options.type ? requireApiTestType(options.type, "--type") : void 0;
2914
+ const filterType = requestedType ? requestedType === "performance" ? "k6" : "playwright" : void 0;
2796
2915
  const files = discoverFiles(cwd, patterns).filter((file) => !filterType || file.type === filterType);
2797
2916
  return files.map((file) => {
2798
2917
  const script = readFileSync4(file.absolutePath, "utf-8");
2799
- const validationType = options.type ? normalizeTestTypeForApi(options.type) : normalizeTestTypeForApi(file.type);
2918
+ const validationType = requestedType ?? normalizeTestTypeForApi(file.type);
2800
2919
  return {
2801
2920
  path: file.absolutePath,
2802
2921
  type: file.type,
@@ -2875,12 +2994,7 @@ testCommand.command("list").description("List all tests").option("--page <page>"
2875
2994
  { key: "createdAt", header: "Created" }
2876
2995
  ]
2877
2996
  });
2878
- if (data.pagination) {
2879
- logger.info(
2880
- `
2881
- Page ${data.pagination.page}/${data.pagination.totalPages} (${data.pagination.total} total)`
2882
- );
2883
- }
2997
+ outputPagination(data.pagination);
2884
2998
  });
2885
2999
  testCommand.command("get <id>").description("Get test details").option("--include-script", "Include the test script content").action(async (id, options) => {
2886
3000
  const client = createAuthenticatedClient();
@@ -2930,21 +3044,44 @@ testCommand.command("create").description("Create a new test").requiredOption("-
2930
3044
  logger.success(`Test "${options.title}" created${createdId ? ` (${createdId})` : ""}`);
2931
3045
  outputDetail(data);
2932
3046
  });
2933
- testCommand.command("update <id>").description("Update a test").option("--title <title>", "Test title").option("--file <path>", "Path to updated test script").option("--description <description>", "Test description").option("--dry-run", "Show what would be sent without updating").action(async (id, options) => {
3047
+ testCommand.command("update <id>").description("Update a test").option("--title <title>", "Test title").option("--file <path>", "Path to updated test script").option("--type <type>", "Test type (browser, performance, api, database, custom)").option("--description <description>", "Test description").option("--dry-run", "Show what would be sent without updating").action(async (id, options) => {
2934
3048
  const client = createAuthenticatedClient();
2935
3049
  const body = {};
2936
3050
  if (options.title !== void 0) body.title = options.title;
2937
3051
  if (options.description !== void 0) body.description = options.description;
3052
+ if (options.type !== void 0) body.type = requireApiTestType(options.type, "--type");
2938
3053
  if (options.file) {
2939
3054
  const { readFileSync: readFileSync6 } = await import("fs");
2940
3055
  const { resolve: resolve10 } = await import("path");
2941
3056
  const filePath = resolve10(process.cwd(), options.file);
3057
+ let raw;
2942
3058
  try {
2943
- const raw = readFileSync6(filePath, "utf-8");
2944
- body.script = Buffer2.from(raw, "utf-8").toString("base64");
3059
+ raw = readFileSync6(filePath, "utf-8");
2945
3060
  } catch {
2946
3061
  throw new CLIError(`Cannot read file: ${filePath}`, 1 /* GeneralError */);
2947
3062
  }
3063
+ const effectiveType = (() => {
3064
+ if (typeof body.type === "string") {
3065
+ return body.type;
3066
+ }
3067
+ return void 0;
3068
+ })();
3069
+ if (!effectiveType) {
3070
+ const { data: existing } = await client.get(`/api/tests/${id}`, {
3071
+ includeScript: "false"
3072
+ });
3073
+ const existingType = typeof existing.type === "string" ? normalizeTestTypeForApi(existing.type) : void 0;
3074
+ const mismatch = validateScriptTypeMatch(raw, existingType);
3075
+ if (mismatch) {
3076
+ throw new CLIError(mismatch, 3 /* ConfigError */);
3077
+ }
3078
+ } else {
3079
+ const mismatch = validateScriptTypeMatch(raw, effectiveType);
3080
+ if (mismatch) {
3081
+ throw new CLIError(mismatch, 3 /* ConfigError */);
3082
+ }
3083
+ }
3084
+ body.script = Buffer2.from(raw, "utf-8").toString("base64");
2948
3085
  }
2949
3086
  if (Object.keys(body).length === 0) {
2950
3087
  logger.warn("No fields to update. Use --title, --file, or --description.");
@@ -2968,7 +3105,7 @@ testCommand.command("update <id>").description("Update a test").option("--title
2968
3105
  });
2969
3106
  testCommand.command("delete <id>").description("Delete a test").option("--force", "Skip confirmation").action(async (id, options) => {
2970
3107
  if (!options.force) {
2971
- const { confirmPrompt: confirmPrompt2 } = await import("../prompt-BPDPYRS7.js");
3108
+ const { confirmPrompt: confirmPrompt2 } = await import("../prompt-L3F2UJRB.js");
2972
3109
  const confirmed = await confirmPrompt2(`Delete test ${id}?`, { default: false });
2973
3110
  if (!confirmed) {
2974
3111
  logger.info("Aborted");
@@ -3053,7 +3190,7 @@ testCommand.command("validate").description("Validate a test script").requiredOp
3053
3190
  throw new CLIError(`Cannot read file: ${filePath}`, 1 /* GeneralError */);
3054
3191
  }
3055
3192
  const typeArg = options.type || inferTestType(options.file) || "playwright";
3056
- const resolvedApiType = normalizeTestTypeForApi(typeArg);
3193
+ const resolvedApiType = options.type ? requireApiTestType(options.type, "--type") : normalizeTestTypeForApi(typeArg);
3057
3194
  const typeMismatchError = validateScriptTypeMatch(script, resolvedApiType);
3058
3195
  if (typeMismatchError) {
3059
3196
  throw new CLIError(typeMismatchError, 3 /* ConfigError */);
@@ -3079,7 +3216,7 @@ testCommand.command("validate").description("Validate a test script").requiredOp
3079
3216
  });
3080
3217
  testCommand.command("status <id>").description("Stream live status events for a test").option("--idle-timeout <seconds>", "Abort if no data received within this period", "60").action(async (id, options) => {
3081
3218
  const token = requireAuth();
3082
- const baseUrl = getStoredBaseUrl() ?? "https://app.supercheck.io";
3219
+ const baseUrl = getStoredBaseUrl() ?? getResolvedConfigBaseUrl() ?? "https://app.supercheck.io";
3083
3220
  const idleTimeoutMs = Math.max(Number(options.idleTimeout) || 60, 10) * 1e3;
3084
3221
  const url = `${baseUrl}/api/test-status/events/${id}`;
3085
3222
  const parsedUrl = new URL(url);
@@ -3152,6 +3289,36 @@ testCommand.command("status <id>").description("Stream live status events for a
3152
3289
 
3153
3290
  // src/commands/monitors.ts
3154
3291
  import { Command as Command8 } from "commander";
3292
+ var VALID_MONITOR_TYPES = ["http_request", "website", "ping_host", "port_check", "synthetic_test"];
3293
+ var VALID_HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
3294
+ function normalizeMonitorType(value) {
3295
+ const normalized = value.trim().toLowerCase();
3296
+ if (!VALID_MONITOR_TYPES.includes(normalized)) {
3297
+ throw new CLIError(
3298
+ `Invalid value for --type: "${value}". Expected one of: ${VALID_MONITOR_TYPES.join(", ")}.`,
3299
+ 3 /* ConfigError */
3300
+ );
3301
+ }
3302
+ return normalized;
3303
+ }
3304
+ function normalizeHttpMethod(value) {
3305
+ const normalized = value.trim().toUpperCase();
3306
+ if (!VALID_HTTP_METHODS.includes(normalized)) {
3307
+ throw new CLIError(
3308
+ `Invalid value for --method: "${value}". Expected one of: ${VALID_HTTP_METHODS.join(", ")}.`,
3309
+ 3 /* ConfigError */
3310
+ );
3311
+ }
3312
+ return normalized;
3313
+ }
3314
+ function getLatestMonitorResponseTimeMs(data) {
3315
+ const recentResults = data.recentResults;
3316
+ if (!Array.isArray(recentResults) || recentResults.length === 0) return void 0;
3317
+ const latest = recentResults[0];
3318
+ if (!latest || typeof latest !== "object") return void 0;
3319
+ const responseTimeMs = latest.responseTimeMs;
3320
+ return typeof responseTimeMs === "number" ? responseTimeMs : void 0;
3321
+ }
3155
3322
  var monitorCommand = new Command8("monitor").description("Manage monitors");
3156
3323
  monitorCommand.command("list").description("List all monitors").option("--page <page>", "Page number", "1").option("--limit <limit>", "Items per page", "50").action(async (options) => {
3157
3324
  const client = createAuthenticatedClient();
@@ -3171,12 +3338,7 @@ monitorCommand.command("list").description("List all monitors").option("--page <
3171
3338
  { key: "frequencyMinutes", header: "Freq (min)" }
3172
3339
  ]
3173
3340
  });
3174
- if (data.pagination) {
3175
- logger.info(
3176
- `
3177
- Page ${data.pagination.page}/${data.pagination.totalPages} (${data.pagination.total} total)`
3178
- );
3179
- }
3341
+ outputPagination(data.pagination);
3180
3342
  });
3181
3343
  monitorCommand.command("get <id>").description("Get monitor details").action(async (id) => {
3182
3344
  const client = createAuthenticatedClient();
@@ -3199,16 +3361,11 @@ monitorCommand.command("results <id>").description("Get monitor check results").
3199
3361
  columns: [
3200
3362
  { key: "checkedAt", header: "Time" },
3201
3363
  { key: "status", header: "Status" },
3202
- { key: "responseTime", header: "Response (ms)" },
3364
+ { key: "responseTimeMs", header: "Response (ms)" },
3203
3365
  { key: "location", header: "Location" }
3204
3366
  ]
3205
3367
  });
3206
- if (data.pagination) {
3207
- logger.info(
3208
- `
3209
- Page ${data.pagination.page}/${data.pagination.totalPages} (${data.pagination.total} total)`
3210
- );
3211
- }
3368
+ outputPagination(data.pagination);
3212
3369
  });
3213
3370
  monitorCommand.command("stats <id>").description("Get monitor performance statistics").action(async (id) => {
3214
3371
  const client = createAuthenticatedClient();
@@ -3250,12 +3407,14 @@ monitorCommand.command("status <id>").description("Get current monitor status").
3250
3407
  name: data.name,
3251
3408
  status: data.status,
3252
3409
  lastCheckAt: data.lastCheckAt,
3253
- responseTime: data.responseTime
3410
+ responseTimeMs: getLatestMonitorResponseTimeMs(data)
3254
3411
  };
3255
3412
  outputDetail(statusInfo);
3256
3413
  });
3257
- monitorCommand.command("create").description("Create a new monitor").requiredOption("--name <name>", "Monitor name").requiredOption("--url <url>", "URL to monitor").option("--type <type>", "Monitor type (http_request, website, ping_host, port_check, synthetic_test)", "http_request").option("--interval-minutes <minutes>", "Check interval in minutes (1-1440)", "5").option("--interval <seconds>", "[deprecated: use --interval-minutes] Check interval in seconds").option("--timeout <seconds>", "Request timeout in seconds", "30").option("--method <method>", "HTTP method (GET, POST, HEAD)", "GET").option("--dry-run", "Show what would be sent without creating").action(async (options) => {
3414
+ monitorCommand.command("create").description("Create a new monitor").requiredOption("--name <name>", "Monitor name").requiredOption("--url <url>", "URL to monitor").option("--type <type>", "Monitor type (http_request, website, ping_host, port_check, synthetic_test)", "http_request").option("--interval-minutes <minutes>", "Check interval in minutes (1-1440)", "5").option("--interval <seconds>", "[deprecated: use --interval-minutes] Check interval in seconds").option("--timeout <seconds>", "Request timeout in seconds", "30").option("--method <method>", "HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)", "GET").option("--dry-run", "Show what would be sent without creating").action(async (options) => {
3258
3415
  const client = createAuthenticatedClient();
3416
+ const monitorType = normalizeMonitorType(options.type);
3417
+ const method = normalizeHttpMethod(options.method);
3259
3418
  let frequencyMinutes;
3260
3419
  if (options.interval !== void 0) {
3261
3420
  logger.warn("--interval (seconds) is deprecated. Use --interval-minutes instead.");
@@ -3273,12 +3432,11 @@ monitorCommand.command("create").description("Create a new monitor").requiredOpt
3273
3432
  const body = {
3274
3433
  name: options.name,
3275
3434
  target: options.url,
3276
- type: options.type,
3435
+ type: monitorType,
3277
3436
  frequencyMinutes,
3278
3437
  config: {
3279
- timeout: parseIntStrict(options.timeout, "--timeout", { min: 1 }) * 1e3,
3280
- // API expects milliseconds
3281
- method: options.method
3438
+ timeoutSeconds: parseIntStrict(options.timeout, "--timeout", { min: 1 }),
3439
+ method
3282
3440
  }
3283
3441
  };
3284
3442
  if (options.dryRun) {
@@ -3293,7 +3451,7 @@ monitorCommand.command("create").description("Create a new monitor").requiredOpt
3293
3451
  logger.success(`Monitor "${options.name}" created (${data.id})`);
3294
3452
  outputDetail(data);
3295
3453
  });
3296
- monitorCommand.command("update <id>").description("Update a monitor").option("--name <name>", "Monitor name").option("--url <url>", "URL to monitor").option("--interval-minutes <minutes>", "Check interval in minutes (1-1440)").option("--interval <seconds>", "[deprecated: use --interval-minutes] Check interval in seconds").option("--timeout <seconds>", "Request timeout in seconds").option("--method <method>", "HTTP method").option("--active <boolean>", "Enable or disable monitor (true/false)").option("--dry-run", "Show what would be sent without updating").action(async (id, options) => {
3454
+ monitorCommand.command("update <id>").description("Update a monitor").option("--name <name>", "Monitor name").option("--url <url>", "URL to monitor").option("--interval-minutes <minutes>", "Check interval in minutes (1-1440)").option("--interval <seconds>", "[deprecated: use --interval-minutes] Check interval in seconds").option("--timeout <seconds>", "Request timeout in seconds").option("--method <method>", "HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)").option("--active <boolean>", "Enable or disable monitor (true/false)").option("--dry-run", "Show what would be sent without updating").action(async (id, options) => {
3297
3455
  const client = createAuthenticatedClient();
3298
3456
  const body = {};
3299
3457
  if (options.name !== void 0) body.name = options.name;
@@ -3311,10 +3469,10 @@ monitorCommand.command("update <id>").description("Update a monitor").option("--
3311
3469
  } else if (options.intervalMinutes !== void 0) {
3312
3470
  body.frequencyMinutes = parseIntStrict(options.intervalMinutes, "--interval-minutes", { min: 1, max: 1440 });
3313
3471
  }
3314
- if (options.active !== void 0) body.enabled = options.active === "true";
3472
+ if (options.active !== void 0) body.enabled = parseBooleanStrict(options.active, "--active");
3315
3473
  const config = {};
3316
- if (options.timeout !== void 0) config.timeout = parseIntStrict(options.timeout, "--timeout", { min: 1 }) * 1e3;
3317
- if (options.method !== void 0) config.method = options.method;
3474
+ if (options.timeout !== void 0) config.timeoutSeconds = parseIntStrict(options.timeout, "--timeout", { min: 1 });
3475
+ if (options.method !== void 0) config.method = normalizeHttpMethod(options.method);
3318
3476
  if (Object.keys(config).length > 0) body.config = config;
3319
3477
  if (Object.keys(body).length === 0) {
3320
3478
  logger.warn("No fields to update. Use --name, --url, --interval-minutes, --timeout, --method, or --active.");
@@ -3334,7 +3492,7 @@ monitorCommand.command("update <id>").description("Update a monitor").option("--
3334
3492
  });
3335
3493
  monitorCommand.command("delete <id>").description("Delete a monitor").option("--force", "Skip confirmation").action(async (id, options) => {
3336
3494
  if (!options.force) {
3337
- const { confirmPrompt: confirmPrompt2 } = await import("../prompt-BPDPYRS7.js");
3495
+ const { confirmPrompt: confirmPrompt2 } = await import("../prompt-L3F2UJRB.js");
3338
3496
  const confirmed = await confirmPrompt2(`Delete monitor ${id}?`, { default: false });
3339
3497
  if (!confirmed) {
3340
3498
  logger.info("Aborted");
@@ -3351,6 +3509,21 @@ monitorCommand.command("delete <id>").description("Delete a monitor").option("--
3351
3509
 
3352
3510
  // src/commands/variables.ts
3353
3511
  import { Command as Command9 } from "commander";
3512
+ import { Buffer as Buffer3 } from "buffer";
3513
+ async function readValueFromStdin() {
3514
+ const MAX_STDIN_BYTES = 1024 * 1024;
3515
+ const chunks = [];
3516
+ let totalBytes = 0;
3517
+ for await (const chunk of process.stdin) {
3518
+ const buf = typeof chunk === "string" ? Buffer3.from(chunk) : chunk;
3519
+ totalBytes += buf.length;
3520
+ if (totalBytes > MAX_STDIN_BYTES) {
3521
+ throw new CLIError("Stdin input exceeds 1 MB limit.", 1 /* GeneralError */);
3522
+ }
3523
+ chunks.push(buf);
3524
+ }
3525
+ return Buffer3.concat(chunks).toString("utf-8").replace(/\r?\n$/, "");
3526
+ }
3354
3527
  var varCommand = new Command9("var").description("Manage project variables");
3355
3528
  varCommand.command("list").description("List all variables").action(async () => {
3356
3529
  const client = createAuthenticatedClient();
@@ -3379,8 +3552,21 @@ varCommand.command("get <key>").description("Get a variable by key name").action
3379
3552
  }
3380
3553
  outputDetail(variable);
3381
3554
  });
3382
- varCommand.command("set <key> <value>").description("Create or update a variable").option("--secret", "Mark as secret (value will be encrypted)").option("--description <description>", "Variable description").action(async (key, value, options) => {
3555
+ varCommand.command("set <key> [value]").description("Create or update a variable").option("--secret", "Mark as secret (value will be encrypted)").option("--value-stdin", "Read the variable value from stdin").option("--description <description>", "Variable description").action(async (key, value, options) => {
3383
3556
  const client = createAuthenticatedClient();
3557
+ if (options.valueStdin && value !== void 0) {
3558
+ throw new CLIError("Use either a positional <value> or --value-stdin, not both.", 3 /* ConfigError */);
3559
+ }
3560
+ let resolvedValue = value;
3561
+ if (options.valueStdin) {
3562
+ resolvedValue = await readValueFromStdin();
3563
+ }
3564
+ if (resolvedValue === void 0) {
3565
+ throw new CLIError("Missing variable value. Provide <value> or pass --value-stdin.", 3 /* ConfigError */);
3566
+ }
3567
+ if (options.secret && value !== void 0) {
3568
+ logger.warn("Passing secret values as CLI arguments exposes them to shell history. Prefer --value-stdin.");
3569
+ }
3384
3570
  const { data: variables } = await withSpinner(
3385
3571
  "Checking existing variables",
3386
3572
  () => client.get("/api/variables")
@@ -3391,7 +3577,7 @@ varCommand.command("set <key> <value>").description("Create or update a variable
3391
3577
  "Updating variable",
3392
3578
  () => client.put(`/api/variables/${existing.id}`, {
3393
3579
  key,
3394
- value,
3580
+ value: resolvedValue,
3395
3581
  isSecret: options.secret ?? existing.isSecret,
3396
3582
  ...options.description !== void 0 && { description: options.description }
3397
3583
  })
@@ -3402,7 +3588,7 @@ varCommand.command("set <key> <value>").description("Create or update a variable
3402
3588
  "Creating variable",
3403
3589
  () => client.post("/api/variables", {
3404
3590
  key,
3405
- value,
3591
+ value: resolvedValue,
3406
3592
  isSecret: options.secret ?? false,
3407
3593
  ...options.description !== void 0 && { description: options.description }
3408
3594
  })
@@ -3421,7 +3607,7 @@ varCommand.command("delete <key>").description("Delete a variable").option("--fo
3421
3607
  throw new CLIError(`Variable "${key}" not found`, 1 /* GeneralError */);
3422
3608
  }
3423
3609
  if (!options.force) {
3424
- const { confirmPrompt: confirmPrompt2 } = await import("../prompt-BPDPYRS7.js");
3610
+ const { confirmPrompt: confirmPrompt2 } = await import("../prompt-L3F2UJRB.js");
3425
3611
  const confirmed = await confirmPrompt2(`Delete variable "${key}"?`, { default: false });
3426
3612
  if (!confirmed) {
3427
3613
  logger.info("Aborted");
@@ -3465,7 +3651,7 @@ tagCommand.command("create <name>").description("Create a new tag").option("--co
3465
3651
  });
3466
3652
  tagCommand.command("delete <id>").description("Delete a tag").option("--force", "Skip confirmation").action(async (id, options) => {
3467
3653
  if (!options.force) {
3468
- const { confirmPrompt: confirmPrompt2 } = await import("../prompt-BPDPYRS7.js");
3654
+ const { confirmPrompt: confirmPrompt2 } = await import("../prompt-L3F2UJRB.js");
3469
3655
  const confirmed = await confirmPrompt2(`Delete tag ${id}?`, { default: false });
3470
3656
  if (!confirmed) {
3471
3657
  logger.info("Aborted");
@@ -3666,6 +3852,7 @@ function prepareBodyForApi(type, body) {
3666
3852
  delete payload.testType;
3667
3853
  }
3668
3854
  if (type === "job") {
3855
+ delete payload.status;
3669
3856
  if (Array.isArray(payload.tests)) {
3670
3857
  payload.tests = payload.tests.map((t) => {
3671
3858
  const match = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i.exec(t);
@@ -4023,7 +4210,7 @@ var destroyCommand = new Command13("destroy").description("Tear down managed res
4023
4210
 
4024
4211
  // src/commands/pull.ts
4025
4212
  import { Command as Command14 } from "commander";
4026
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync as writeFileSync4, readFileSync as readFileSync5, readdirSync as readdirSync2, unlinkSync } from "fs";
4213
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync as writeFileSync4, readFileSync as readFileSync5, readdirSync as readdirSync3, unlinkSync } from "fs";
4027
4214
  import { resolve as resolve9, dirname as dirname2 } from "path";
4028
4215
  import pc7 from "picocolors";
4029
4216
  function mapTestType(apiType) {
@@ -4136,7 +4323,7 @@ function pullTests(tests, cwd, summary) {
4136
4323
  for (const folder of ["playwright", "k6"]) {
4137
4324
  const folderPath = resolve9(baseDir, folder);
4138
4325
  if (!existsSync5(folderPath)) continue;
4139
- for (const file of readdirSync2(folderPath)) {
4326
+ for (const file of readdirSync3(folderPath)) {
4140
4327
  const uuid = extractUuidFromFilename(file);
4141
4328
  if (uuid) existingByUuid.set(uuid, `${folder}/${file}`);
4142
4329
  }
@@ -4312,6 +4499,7 @@ function buildStatusPageDefinitions(pages) {
4312
4499
  if (p.pageDescription) def.description = p.pageDescription;
4313
4500
  if (p.headline) def.headline = p.headline;
4314
4501
  if (p.supportUrl) def.supportUrl = p.supportUrl;
4502
+ if (p.language && p.language !== "en") def.language = p.language;
4315
4503
  return def;
4316
4504
  });
4317
4505
  }
@@ -4399,7 +4587,7 @@ function generateConfigContent(opts) {
4399
4587
  parts.push(" *");
4400
4588
  parts.push(" * supercheck test list List all tests");
4401
4589
  parts.push(" * supercheck test validate Validate a local test script");
4402
- parts.push(" * supercheck test run --local Run local test scripts");
4590
+ parts.push(" * supercheck test run Run local test scripts");
4403
4591
  parts.push(" *");
4404
4592
  parts.push(" * supercheck monitor list List all monitors");
4405
4593
  parts.push(" * supercheck job list List all jobs");
@@ -4544,7 +4732,7 @@ var pullCommand = new Command14("pull").description("Pull tests, monitors, jobs,
4544
4732
  logger.info(`Found ${pc7.bold(String(totalResources))} resources to pull.`);
4545
4733
  logger.info("This will write test scripts and update supercheck.config.ts.");
4546
4734
  logger.newline();
4547
- const { confirmPrompt: confirmPrompt2 } = await import("../prompt-BPDPYRS7.js");
4735
+ const { confirmPrompt: confirmPrompt2 } = await import("../prompt-L3F2UJRB.js");
4548
4736
  const confirmed = await confirmPrompt2("Continue?", { default: true });
4549
4737
  if (!confirmed) {
4550
4738
  logger.info("Pull aborted.");
@@ -4868,7 +5056,7 @@ notificationCommand.command("update <id>").description("Update a notification pr
4868
5056
  });
4869
5057
  notificationCommand.command("delete <id>").description("Delete a notification provider").option("--force", "Skip confirmation").action(async (id, options) => {
4870
5058
  if (!options.force) {
4871
- const { confirmPrompt: confirmPrompt2 } = await import("../prompt-BPDPYRS7.js");
5059
+ const { confirmPrompt: confirmPrompt2 } = await import("../prompt-L3F2UJRB.js");
4872
5060
  const confirmed = await confirmPrompt2(`Delete notification provider ${id}?`, { default: false });
4873
5061
  if (!confirmed) {
4874
5062
  logger.info("Aborted");
@@ -4932,12 +5120,7 @@ alertCommand.command("history").description("Get alert history").option("--page
4932
5120
  { key: "timestamp", header: "Time" }
4933
5121
  ]
4934
5122
  });
4935
- if (pagination) {
4936
- logger.info(
4937
- `
4938
- Page ${pagination.page}/${pagination.totalPages} (${pagination.total} total)`
4939
- );
4940
- }
5123
+ outputPagination(pagination);
4941
5124
  });
4942
5125
 
4943
5126
  // src/commands/audit.ts
@@ -4971,13 +5154,7 @@ var auditCommand = new Command18("audit").description("View audit logs (admin)")
4971
5154
  { key: "createdAt", header: "Date" }
4972
5155
  ]
4973
5156
  });
4974
- const { pagination } = data.data;
4975
- if (pagination) {
4976
- logger.info(
4977
- `
4978
- Page ${pagination.currentPage}/${pagination.totalPages} (${pagination.totalCount} total)`
4979
- );
4980
- }
5157
+ outputPagination(data.data.pagination);
4981
5158
  });
4982
5159
 
4983
5160
  // src/commands/doctor.ts
@@ -5200,7 +5377,7 @@ var upgradeCommand = new Command20("upgrade").description("Upgrade the Superchec
5200
5377
  });
5201
5378
 
5202
5379
  // src/bin/supercheck.ts
5203
- var program = new Command21().name("supercheck").description("Open-source testing, monitoring, and reliability \u2014 as code").version(CLI_VERSION, "-v, --version").option("--json", "Output in JSON format").option("--quiet", "Suppress non-essential output").option("--debug", "Enable debug logging").hook("preAction", (_thisCommand, actionCommand) => {
5380
+ var program = new Command21().name("supercheck").description("Open-source testing, monitoring, and reliability \u2014 as code").version(CLI_VERSION, "-v, --version").option("--json", "Output in JSON format").option("--quiet", "Suppress non-essential output").option("--debug", "Enable debug logging").hook("preAction", async (_thisCommand, actionCommand) => {
5204
5381
  const opts = program.opts();
5205
5382
  if (opts.json) {
5206
5383
  setOutputFormat("json");
@@ -5212,7 +5389,9 @@ var program = new Command21().name("supercheck").description("Open-source testin
5212
5389
  if (opts.debug) {
5213
5390
  setLogLevel("debug");
5214
5391
  }
5215
- void actionCommand;
5392
+ const actionOpts = typeof actionCommand.opts === "function" ? actionCommand.opts() : {};
5393
+ const configPath = typeof actionOpts.config === "string" ? actionOpts.config : void 0;
5394
+ await resolveAndSetConfigBaseUrl({ configPath });
5216
5395
  });
5217
5396
  program.addCommand(loginCommand);
5218
5397
  program.addCommand(logoutCommand);