@supercheck/cli 0.1.0-beta.5 → 0.1.0-beta.7
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/dist/bin/supercheck.js +310 -177
- package/dist/bin/supercheck.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/supercheck.js
CHANGED
|
@@ -213,7 +213,7 @@ function requireTriggerKey() {
|
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
// src/version.ts
|
|
216
|
-
var CLI_VERSION = true ? "0.1.0-beta.
|
|
216
|
+
var CLI_VERSION = true ? "0.1.0-beta.7" : "0.0.0-dev";
|
|
217
217
|
|
|
218
218
|
// src/api/client.ts
|
|
219
219
|
import { ProxyAgent } from "undici";
|
|
@@ -1485,15 +1485,6 @@ Error: ${message}`,
|
|
|
1485
1485
|
{ successText: "Dependencies installed" }
|
|
1486
1486
|
);
|
|
1487
1487
|
}
|
|
1488
|
-
async function checkK6Binary() {
|
|
1489
|
-
const { execSync } = await import("child_process");
|
|
1490
|
-
try {
|
|
1491
|
-
execSync("k6 version", { stdio: "ignore" });
|
|
1492
|
-
return true;
|
|
1493
|
-
} catch {
|
|
1494
|
-
return false;
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
1488
|
|
|
1498
1489
|
// src/commands/init.ts
|
|
1499
1490
|
var CONFIG_TEMPLATE = `import { defineConfig } from '@supercheck/cli'
|
|
@@ -1692,9 +1683,12 @@ function parseIntStrict(value, name, opts) {
|
|
|
1692
1683
|
var jobCommand = new Command5("job").description("Manage jobs");
|
|
1693
1684
|
jobCommand.command("list").description("List all jobs").option("--page <page>", "Page number", "1").option("--limit <limit>", "Items per page", "50").action(async (options) => {
|
|
1694
1685
|
const client = createAuthenticatedClient();
|
|
1695
|
-
const { data } = await
|
|
1696
|
-
"
|
|
1697
|
-
|
|
1686
|
+
const { data } = await withSpinner(
|
|
1687
|
+
"Fetching jobs",
|
|
1688
|
+
() => client.get(
|
|
1689
|
+
"/api/jobs",
|
|
1690
|
+
{ page: options.page, limit: options.limit }
|
|
1691
|
+
)
|
|
1698
1692
|
);
|
|
1699
1693
|
output(data.data, {
|
|
1700
1694
|
columns: [
|
|
@@ -1715,8 +1709,11 @@ Page ${data.pagination.page}/${data.pagination.totalPages} (${data.pagination.to
|
|
|
1715
1709
|
var keysCommand = jobCommand.command("keys").description("Manage job trigger keys");
|
|
1716
1710
|
keysCommand.argument("<jobId>", "Job ID").action(async (jobId) => {
|
|
1717
1711
|
const client = createAuthenticatedClient();
|
|
1718
|
-
const { data } = await
|
|
1719
|
-
|
|
1712
|
+
const { data } = await withSpinner(
|
|
1713
|
+
"Fetching trigger keys",
|
|
1714
|
+
() => client.get(
|
|
1715
|
+
`/api/jobs/${jobId}/api-keys`
|
|
1716
|
+
)
|
|
1720
1717
|
);
|
|
1721
1718
|
output(data.apiKeys ?? [], {
|
|
1722
1719
|
columns: [
|
|
@@ -1735,9 +1732,12 @@ keysCommand.command("create").description("Create a new trigger key for a job").
|
|
|
1735
1732
|
if (options.expiresIn !== void 0) {
|
|
1736
1733
|
body.expiresIn = parseIntStrict(options.expiresIn, "--expires-in", { min: 60 });
|
|
1737
1734
|
}
|
|
1738
|
-
const { data } = await
|
|
1739
|
-
|
|
1740
|
-
|
|
1735
|
+
const { data } = await withSpinner(
|
|
1736
|
+
"Creating trigger key",
|
|
1737
|
+
() => client.post(
|
|
1738
|
+
`/api/jobs/${jobId}/api-keys`,
|
|
1739
|
+
body
|
|
1740
|
+
)
|
|
1741
1741
|
);
|
|
1742
1742
|
logger.success("Trigger key created");
|
|
1743
1743
|
logger.warn("Save the `key` value now. It will only be shown once.");
|
|
@@ -1745,12 +1745,18 @@ keysCommand.command("create").description("Create a new trigger key for a job").
|
|
|
1745
1745
|
});
|
|
1746
1746
|
keysCommand.command("delete").description("Revoke a trigger key").argument("<jobId>", "Job ID").argument("<keyId>", "Key ID").action(async (jobId, keyId) => {
|
|
1747
1747
|
const client = createAuthenticatedClient();
|
|
1748
|
-
await
|
|
1748
|
+
await withSpinner(
|
|
1749
|
+
"Revoking trigger key",
|
|
1750
|
+
() => client.delete(`/api/jobs/${jobId}/api-keys/${keyId}`)
|
|
1751
|
+
);
|
|
1749
1752
|
logger.success(`Trigger key ${keyId} revoked`);
|
|
1750
1753
|
});
|
|
1751
1754
|
jobCommand.command("get <id>").description("Get job details").action(async (id) => {
|
|
1752
1755
|
const client = createAuthenticatedClient();
|
|
1753
|
-
const { data } = await
|
|
1756
|
+
const { data } = await withSpinner(
|
|
1757
|
+
"Fetching job details",
|
|
1758
|
+
() => client.get(`/api/jobs/${id}`)
|
|
1759
|
+
);
|
|
1754
1760
|
outputDetail(data);
|
|
1755
1761
|
});
|
|
1756
1762
|
jobCommand.command("create").description("Create a new job").requiredOption("--name <name>", "Job name").option("--description <description>", "Job description", "").option("--type <type>", "Job type (playwright, k6)").option("--schedule <cron>", "Cron schedule expression").option("--timeout <seconds>", "Timeout in seconds", "300").option("--retries <count>", "Retry count on failure", "0").action(async (options) => {
|
|
@@ -1765,7 +1771,10 @@ jobCommand.command("create").description("Create a new job").requiredOption("--n
|
|
|
1765
1771
|
};
|
|
1766
1772
|
if (options.type) body.jobType = options.type;
|
|
1767
1773
|
if (options.schedule) body.cronSchedule = options.schedule;
|
|
1768
|
-
const { data } = await
|
|
1774
|
+
const { data } = await withSpinner(
|
|
1775
|
+
"Creating job",
|
|
1776
|
+
() => client.post("/api/jobs", body)
|
|
1777
|
+
);
|
|
1769
1778
|
logger.success(`Job "${options.name}" created (${data.id})`);
|
|
1770
1779
|
outputDetail(data);
|
|
1771
1780
|
});
|
|
@@ -1782,7 +1791,10 @@ jobCommand.command("update <id>").description("Update job configuration").option
|
|
|
1782
1791
|
logger.warn("No fields to update. Use --name, --description, --schedule, --timeout, --retries, or --status.");
|
|
1783
1792
|
return;
|
|
1784
1793
|
}
|
|
1785
|
-
const { data } = await
|
|
1794
|
+
const { data } = await withSpinner(
|
|
1795
|
+
"Updating job",
|
|
1796
|
+
() => client.patch(`/api/jobs/${id}`, body)
|
|
1797
|
+
);
|
|
1786
1798
|
logger.success(`Job ${id} updated`);
|
|
1787
1799
|
outputDetail(data);
|
|
1788
1800
|
});
|
|
@@ -1796,13 +1808,19 @@ jobCommand.command("delete <id>").description("Delete a job").option("--force",
|
|
|
1796
1808
|
}
|
|
1797
1809
|
}
|
|
1798
1810
|
const client = createAuthenticatedClient();
|
|
1799
|
-
await
|
|
1811
|
+
await withSpinner(
|
|
1812
|
+
"Deleting job",
|
|
1813
|
+
() => client.delete(`/api/jobs/${id}`)
|
|
1814
|
+
);
|
|
1800
1815
|
logger.success(`Job ${id} deleted`);
|
|
1801
1816
|
});
|
|
1802
1817
|
jobCommand.command("run").description("Run a job immediately").requiredOption("--id <id>", "Job ID to run").action(async (options) => {
|
|
1803
1818
|
const client = createAuthenticatedClient();
|
|
1804
|
-
const { data: jobData } = await
|
|
1805
|
-
|
|
1819
|
+
const { data: jobData } = await withSpinner(
|
|
1820
|
+
"Fetching job details",
|
|
1821
|
+
() => client.get(
|
|
1822
|
+
`/api/jobs/${options.id}`
|
|
1823
|
+
)
|
|
1806
1824
|
);
|
|
1807
1825
|
const tests = Array.isArray(jobData.tests) ? jobData.tests : [];
|
|
1808
1826
|
if (tests.length === 0) {
|
|
@@ -1815,10 +1833,12 @@ jobCommand.command("run").description("Run a job immediately").requiredOption("-
|
|
|
1815
1833
|
id: test.id,
|
|
1816
1834
|
name: test.name ?? test.title ?? ""
|
|
1817
1835
|
}));
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1836
|
+
const { data } = await withSpinner(
|
|
1837
|
+
"Running job",
|
|
1838
|
+
() => client.post(
|
|
1839
|
+
"/api/jobs/run",
|
|
1840
|
+
{ jobId: options.id, tests: payloadTests, trigger: "manual" }
|
|
1841
|
+
)
|
|
1822
1842
|
);
|
|
1823
1843
|
logger.success(`Job started. Run ID: ${data.runId}`);
|
|
1824
1844
|
outputDetail(data);
|
|
@@ -1828,9 +1848,11 @@ jobCommand.command("trigger <id>").description("Trigger a job run").option("--wa
|
|
|
1828
1848
|
token: requireTriggerKey(),
|
|
1829
1849
|
baseUrl: getStoredBaseUrl() ?? void 0
|
|
1830
1850
|
});
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1851
|
+
const { data } = await withSpinner(
|
|
1852
|
+
"Triggering job",
|
|
1853
|
+
() => triggerClient.post(
|
|
1854
|
+
`/api/jobs/${id}/trigger`
|
|
1855
|
+
)
|
|
1834
1856
|
);
|
|
1835
1857
|
logger.success(`Job triggered. Run ID: ${data.runId}`);
|
|
1836
1858
|
if (options.wait) {
|
|
@@ -1919,17 +1941,19 @@ function getProxyEnv(url) {
|
|
|
1919
1941
|
return null;
|
|
1920
1942
|
}
|
|
1921
1943
|
var runCommand = new Command6("run").description("Manage runs");
|
|
1922
|
-
runCommand.command("list").description("List runs").option("--page <page>", "Page number", "1").option("--limit <limit>", "Items per page", "50").option("--
|
|
1944
|
+
runCommand.command("list").description("List runs").option("--page <page>", "Page number", "1").option("--limit <limit>", "Items per page", "50").option("--status <status>", "Filter by status").action(async (options) => {
|
|
1923
1945
|
const client = createAuthenticatedClient();
|
|
1924
1946
|
const params = {
|
|
1925
1947
|
page: options.page,
|
|
1926
1948
|
limit: options.limit
|
|
1927
1949
|
};
|
|
1928
|
-
if (options.job) params.jobId = options.job;
|
|
1929
1950
|
if (options.status) params.status = options.status;
|
|
1930
|
-
const { data } = await
|
|
1931
|
-
"
|
|
1932
|
-
|
|
1951
|
+
const { data } = await withSpinner(
|
|
1952
|
+
"Fetching runs",
|
|
1953
|
+
() => client.get(
|
|
1954
|
+
"/api/runs",
|
|
1955
|
+
params
|
|
1956
|
+
)
|
|
1933
1957
|
);
|
|
1934
1958
|
output(data.data, {
|
|
1935
1959
|
columns: [
|
|
@@ -1937,7 +1961,6 @@ runCommand.command("list").description("List runs").option("--page <page>", "Pag
|
|
|
1937
1961
|
{ key: "jobName", header: "Job" },
|
|
1938
1962
|
{ key: "status", header: "Status" },
|
|
1939
1963
|
{ key: "trigger", header: "Trigger" },
|
|
1940
|
-
{ key: "duration", header: "Duration" },
|
|
1941
1964
|
{ key: "startedAt", header: "Started" }
|
|
1942
1965
|
]
|
|
1943
1966
|
});
|
|
@@ -1950,22 +1973,18 @@ Page ${data.pagination.page}/${data.pagination.totalPages} (${data.pagination.to
|
|
|
1950
1973
|
});
|
|
1951
1974
|
runCommand.command("get <id>").description("Get run details").action(async (id) => {
|
|
1952
1975
|
const client = createAuthenticatedClient();
|
|
1953
|
-
const { data } = await
|
|
1954
|
-
|
|
1955
|
-
})
|
|
1956
|
-
|
|
1957
|
-
const client = createAuthenticatedClient();
|
|
1958
|
-
const { data } = await client.get(`/api/runs/${id}/permissions`);
|
|
1976
|
+
const { data } = await withSpinner(
|
|
1977
|
+
"Fetching run details",
|
|
1978
|
+
() => client.get(`/api/runs/${id}`)
|
|
1979
|
+
);
|
|
1959
1980
|
outputDetail(data);
|
|
1960
1981
|
});
|
|
1961
|
-
runCommand.command("cancel <id>").description("Cancel a running execution").action(async (id) => {
|
|
1962
|
-
const client = createAuthenticatedClient();
|
|
1963
|
-
await client.post(`/api/runs/${id}/cancel`);
|
|
1964
|
-
logger.success(`Run ${id} cancelled`);
|
|
1965
|
-
});
|
|
1966
1982
|
runCommand.command("status <id>").description("Get run status").action(async (id) => {
|
|
1967
1983
|
const client = createAuthenticatedClient();
|
|
1968
|
-
const { data } = await
|
|
1984
|
+
const { data } = await withSpinner(
|
|
1985
|
+
"Fetching run status",
|
|
1986
|
+
() => client.get(`/api/runs/${id}/status`)
|
|
1987
|
+
);
|
|
1969
1988
|
outputDetail(data);
|
|
1970
1989
|
});
|
|
1971
1990
|
runCommand.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) => {
|
|
@@ -2030,18 +2049,13 @@ runCommand.command("stream <id>").description("Stream live console output from a
|
|
|
2030
2049
|
process.stdout.write(parsed.line + "\n");
|
|
2031
2050
|
}
|
|
2032
2051
|
break;
|
|
2052
|
+
case "status":
|
|
2053
|
+
logger.info(`Status: ${parsed.status ?? JSON.stringify(parsed)}`);
|
|
2054
|
+
break;
|
|
2033
2055
|
case "complete":
|
|
2034
|
-
logger.
|
|
2035
|
-
if (parsed.status === "completed" || parsed.status === "success") {
|
|
2036
|
-
logger.success(`Run ${id} ${parsed.status}`);
|
|
2037
|
-
} else {
|
|
2038
|
-
logger.warn(`Run ${id} finished with status: ${parsed.status}`);
|
|
2039
|
-
}
|
|
2056
|
+
logger.success(`Run complete: ${parsed.status ?? "done"}`);
|
|
2040
2057
|
if (idleTimer) clearTimeout(idleTimer);
|
|
2041
2058
|
return;
|
|
2042
|
-
case "ready":
|
|
2043
|
-
logger.debug(`Stream ready for run ${parsed.runId ?? id}`);
|
|
2044
|
-
break;
|
|
2045
2059
|
case "error":
|
|
2046
2060
|
logger.error(`Stream error: ${parsed.message ?? JSON.stringify(parsed)}`);
|
|
2047
2061
|
break;
|
|
@@ -2144,9 +2158,12 @@ testCommand.command("list").description("List all tests").option("--page <page>"
|
|
|
2144
2158
|
};
|
|
2145
2159
|
if (options.search) params.search = options.search;
|
|
2146
2160
|
if (options.type) params.type = normalizeTestType(options.type);
|
|
2147
|
-
const { data } = await
|
|
2148
|
-
"
|
|
2149
|
-
|
|
2161
|
+
const { data } = await withSpinner(
|
|
2162
|
+
"Fetching tests",
|
|
2163
|
+
() => client.get(
|
|
2164
|
+
"/api/tests",
|
|
2165
|
+
params
|
|
2166
|
+
)
|
|
2150
2167
|
);
|
|
2151
2168
|
output(data.data, {
|
|
2152
2169
|
columns: [
|
|
@@ -2167,7 +2184,10 @@ testCommand.command("get <id>").description("Get test details").option("--includ
|
|
|
2167
2184
|
const client = createAuthenticatedClient();
|
|
2168
2185
|
const params = {};
|
|
2169
2186
|
if (options.includeScript) params.includeScript = "true";
|
|
2170
|
-
const { data } = await
|
|
2187
|
+
const { data } = await withSpinner(
|
|
2188
|
+
"Fetching test details",
|
|
2189
|
+
() => client.get(`/api/tests/${id}`, params)
|
|
2190
|
+
);
|
|
2171
2191
|
outputDetail(data);
|
|
2172
2192
|
});
|
|
2173
2193
|
testCommand.command("create").description("Create a new test").requiredOption("--title <title>", "Test title").requiredOption("--file <path>", "Path to the test script file").option("--type <type>", "Test type (playwright, k6)", "playwright").option("--description <description>", "Test description").action(async (options) => {
|
|
@@ -2188,7 +2208,10 @@ testCommand.command("create").description("Create a new test").requiredOption("-
|
|
|
2188
2208
|
type: normalizeTestType(options.type)
|
|
2189
2209
|
};
|
|
2190
2210
|
if (options.description) body.description = options.description;
|
|
2191
|
-
const { data } = await
|
|
2211
|
+
const { data } = await withSpinner(
|
|
2212
|
+
"Creating test",
|
|
2213
|
+
() => client.post("/api/tests", body)
|
|
2214
|
+
);
|
|
2192
2215
|
const createdId = data.test?.id ?? data.id;
|
|
2193
2216
|
logger.success(`Test "${options.title}" created${createdId ? ` (${createdId})` : ""}`);
|
|
2194
2217
|
outputDetail(data);
|
|
@@ -2213,7 +2236,10 @@ testCommand.command("update <id>").description("Update a test").option("--title
|
|
|
2213
2236
|
logger.warn("No fields to update. Use --title, --file, or --description.");
|
|
2214
2237
|
return;
|
|
2215
2238
|
}
|
|
2216
|
-
const { data } = await
|
|
2239
|
+
const { data } = await withSpinner(
|
|
2240
|
+
"Updating test",
|
|
2241
|
+
() => client.patch(`/api/tests/${id}`, body)
|
|
2242
|
+
);
|
|
2217
2243
|
logger.success(`Test ${id} updated`);
|
|
2218
2244
|
outputDetail(data);
|
|
2219
2245
|
});
|
|
@@ -2227,19 +2253,28 @@ testCommand.command("delete <id>").description("Delete a test").option("--force"
|
|
|
2227
2253
|
}
|
|
2228
2254
|
}
|
|
2229
2255
|
const client = createAuthenticatedClient();
|
|
2230
|
-
await
|
|
2256
|
+
await withSpinner(
|
|
2257
|
+
"Deleting test",
|
|
2258
|
+
() => client.delete(`/api/tests/${id}`)
|
|
2259
|
+
);
|
|
2231
2260
|
logger.success(`Test ${id} deleted`);
|
|
2232
2261
|
});
|
|
2233
2262
|
testCommand.command("execute <id>").description("Execute a test immediately").option("--location <location>", "Execution location (k6 only)").action(async (id, options) => {
|
|
2234
2263
|
const client = createAuthenticatedClient();
|
|
2235
2264
|
const body = {};
|
|
2236
2265
|
if (options.location) body.location = options.location;
|
|
2237
|
-
const { data } = await
|
|
2266
|
+
const { data } = await withSpinner(
|
|
2267
|
+
"Executing test",
|
|
2268
|
+
() => client.post(`/api/tests/${id}/execute`, body)
|
|
2269
|
+
);
|
|
2238
2270
|
outputDetail(data);
|
|
2239
2271
|
});
|
|
2240
2272
|
testCommand.command("tags <id>").description("Get test tags").action(async (id) => {
|
|
2241
2273
|
const client = createAuthenticatedClient();
|
|
2242
|
-
const { data } = await
|
|
2274
|
+
const { data } = await withSpinner(
|
|
2275
|
+
"Fetching test tags",
|
|
2276
|
+
() => client.get(`/api/tests/${id}/tags`)
|
|
2277
|
+
);
|
|
2243
2278
|
output(data, {
|
|
2244
2279
|
columns: [
|
|
2245
2280
|
{ key: "id", header: "ID" },
|
|
@@ -2259,9 +2294,12 @@ testCommand.command("validate").description("Validate a test script").requiredOp
|
|
|
2259
2294
|
throw new CLIError(`Cannot read file: ${filePath}`, 1 /* GeneralError */);
|
|
2260
2295
|
}
|
|
2261
2296
|
const client = createAuthenticatedClient();
|
|
2262
|
-
const { data } = await
|
|
2263
|
-
"
|
|
2264
|
-
|
|
2297
|
+
const { data } = await withSpinner(
|
|
2298
|
+
"Validating script",
|
|
2299
|
+
() => client.post(
|
|
2300
|
+
"/api/validate-script",
|
|
2301
|
+
{ script, testType: normalizeTestType(options.type) }
|
|
2302
|
+
)
|
|
2265
2303
|
);
|
|
2266
2304
|
if (data.valid) {
|
|
2267
2305
|
logger.success(`Script is valid (${options.type})`);
|
|
@@ -2353,9 +2391,12 @@ import { Command as Command8 } from "commander";
|
|
|
2353
2391
|
var monitorCommand = new Command8("monitor").description("Manage monitors");
|
|
2354
2392
|
monitorCommand.command("list").description("List all monitors").option("--page <page>", "Page number", "1").option("--limit <limit>", "Items per page", "50").action(async (options) => {
|
|
2355
2393
|
const client = createAuthenticatedClient();
|
|
2356
|
-
const { data } = await
|
|
2357
|
-
"
|
|
2358
|
-
|
|
2394
|
+
const { data } = await withSpinner(
|
|
2395
|
+
"Fetching monitors",
|
|
2396
|
+
() => client.get(
|
|
2397
|
+
"/api/monitors",
|
|
2398
|
+
{ page: options.page, limit: options.limit }
|
|
2399
|
+
)
|
|
2359
2400
|
);
|
|
2360
2401
|
output(data.data, {
|
|
2361
2402
|
columns: [
|
|
@@ -2375,14 +2416,20 @@ Page ${data.pagination.page}/${data.pagination.totalPages} (${data.pagination.to
|
|
|
2375
2416
|
});
|
|
2376
2417
|
monitorCommand.command("get <id>").description("Get monitor details").action(async (id) => {
|
|
2377
2418
|
const client = createAuthenticatedClient();
|
|
2378
|
-
const { data } = await
|
|
2419
|
+
const { data } = await withSpinner(
|
|
2420
|
+
"Fetching monitor details",
|
|
2421
|
+
() => client.get(`/api/monitors/${id}`)
|
|
2422
|
+
);
|
|
2379
2423
|
outputDetail(data);
|
|
2380
2424
|
});
|
|
2381
2425
|
monitorCommand.command("results <id>").description("Get monitor check results").option("--limit <limit>", "Number of results", "20").action(async (id, options) => {
|
|
2382
2426
|
const client = createAuthenticatedClient();
|
|
2383
|
-
const { data } = await
|
|
2384
|
-
|
|
2385
|
-
|
|
2427
|
+
const { data } = await withSpinner(
|
|
2428
|
+
"Fetching monitor results",
|
|
2429
|
+
() => client.get(
|
|
2430
|
+
`/api/monitors/${id}/results`,
|
|
2431
|
+
{ limit: options.limit }
|
|
2432
|
+
)
|
|
2386
2433
|
);
|
|
2387
2434
|
output(data.results, {
|
|
2388
2435
|
columns: [
|
|
@@ -2395,12 +2442,18 @@ monitorCommand.command("results <id>").description("Get monitor check results").
|
|
|
2395
2442
|
});
|
|
2396
2443
|
monitorCommand.command("stats <id>").description("Get monitor performance statistics").action(async (id) => {
|
|
2397
2444
|
const client = createAuthenticatedClient();
|
|
2398
|
-
const { data } = await
|
|
2445
|
+
const { data } = await withSpinner(
|
|
2446
|
+
"Fetching monitor statistics",
|
|
2447
|
+
() => client.get(`/api/monitors/${id}/stats`)
|
|
2448
|
+
);
|
|
2399
2449
|
outputDetail(data);
|
|
2400
2450
|
});
|
|
2401
2451
|
monitorCommand.command("status <id>").description("Get current monitor status").action(async (id) => {
|
|
2402
2452
|
const client = createAuthenticatedClient();
|
|
2403
|
-
const { data } = await
|
|
2453
|
+
const { data } = await withSpinner(
|
|
2454
|
+
"Fetching monitor status",
|
|
2455
|
+
() => client.get(`/api/monitors/${id}`)
|
|
2456
|
+
);
|
|
2404
2457
|
const statusInfo = {
|
|
2405
2458
|
id: data.id,
|
|
2406
2459
|
name: data.name,
|
|
@@ -2425,7 +2478,10 @@ monitorCommand.command("create").description("Create a new monitor").requiredOpt
|
|
|
2425
2478
|
method: options.method
|
|
2426
2479
|
}
|
|
2427
2480
|
};
|
|
2428
|
-
const { data } = await
|
|
2481
|
+
const { data } = await withSpinner(
|
|
2482
|
+
"Creating monitor",
|
|
2483
|
+
() => client.post("/api/monitors", body)
|
|
2484
|
+
);
|
|
2429
2485
|
logger.success(`Monitor "${options.name}" created (${data.id})`);
|
|
2430
2486
|
outputDetail(data);
|
|
2431
2487
|
});
|
|
@@ -2447,7 +2503,10 @@ monitorCommand.command("update <id>").description("Update a monitor").option("--
|
|
|
2447
2503
|
logger.warn("No fields to update. Use --name, --url, --interval, --timeout, --method, or --active.");
|
|
2448
2504
|
return;
|
|
2449
2505
|
}
|
|
2450
|
-
const { data } = await
|
|
2506
|
+
const { data } = await withSpinner(
|
|
2507
|
+
"Updating monitor",
|
|
2508
|
+
() => client.patch(`/api/monitors/${id}`, body)
|
|
2509
|
+
);
|
|
2451
2510
|
logger.success(`Monitor ${id} updated`);
|
|
2452
2511
|
outputDetail(data);
|
|
2453
2512
|
});
|
|
@@ -2461,7 +2520,10 @@ monitorCommand.command("delete <id>").description("Delete a monitor").option("--
|
|
|
2461
2520
|
}
|
|
2462
2521
|
}
|
|
2463
2522
|
const client = createAuthenticatedClient();
|
|
2464
|
-
await
|
|
2523
|
+
await withSpinner(
|
|
2524
|
+
"Deleting monitor",
|
|
2525
|
+
() => client.delete(`/api/monitors/${id}`)
|
|
2526
|
+
);
|
|
2465
2527
|
logger.success(`Monitor ${id} deleted`);
|
|
2466
2528
|
});
|
|
2467
2529
|
|
|
@@ -2470,7 +2532,10 @@ import { Command as Command9 } from "commander";
|
|
|
2470
2532
|
var varCommand = new Command9("var").description("Manage project variables");
|
|
2471
2533
|
varCommand.command("list").description("List all variables").action(async () => {
|
|
2472
2534
|
const client = createAuthenticatedClient();
|
|
2473
|
-
const { data } = await
|
|
2535
|
+
const { data } = await withSpinner(
|
|
2536
|
+
"Fetching variables",
|
|
2537
|
+
() => client.get("/api/variables")
|
|
2538
|
+
);
|
|
2474
2539
|
output(data, {
|
|
2475
2540
|
columns: [
|
|
2476
2541
|
{ key: "id", header: "ID" },
|
|
@@ -2482,7 +2547,10 @@ varCommand.command("list").description("List all variables").action(async () =>
|
|
|
2482
2547
|
});
|
|
2483
2548
|
varCommand.command("get <key>").description("Get a variable by key name").action(async (key) => {
|
|
2484
2549
|
const client = createAuthenticatedClient();
|
|
2485
|
-
const { data: variables } = await
|
|
2550
|
+
const { data: variables } = await withSpinner(
|
|
2551
|
+
"Fetching variable",
|
|
2552
|
+
() => client.get("/api/variables")
|
|
2553
|
+
);
|
|
2486
2554
|
const variable = variables.find((v) => v.key === key);
|
|
2487
2555
|
if (!variable) {
|
|
2488
2556
|
throw new CLIError(`Variable "${key}" not found`, 1 /* GeneralError */);
|
|
@@ -2491,29 +2559,41 @@ varCommand.command("get <key>").description("Get a variable by key name").action
|
|
|
2491
2559
|
});
|
|
2492
2560
|
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) => {
|
|
2493
2561
|
const client = createAuthenticatedClient();
|
|
2494
|
-
const { data: variables } = await
|
|
2562
|
+
const { data: variables } = await withSpinner(
|
|
2563
|
+
"Checking existing variables",
|
|
2564
|
+
() => client.get("/api/variables")
|
|
2565
|
+
);
|
|
2495
2566
|
const existing = variables.find((v) => v.key === key);
|
|
2496
2567
|
if (existing) {
|
|
2497
|
-
await
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2568
|
+
await withSpinner(
|
|
2569
|
+
"Updating variable",
|
|
2570
|
+
() => client.put(`/api/variables/${existing.id}`, {
|
|
2571
|
+
key,
|
|
2572
|
+
value,
|
|
2573
|
+
isSecret: options.secret ?? existing.isSecret,
|
|
2574
|
+
...options.description !== void 0 && { description: options.description }
|
|
2575
|
+
})
|
|
2576
|
+
);
|
|
2503
2577
|
logger.success(`Variable "${key}" updated`);
|
|
2504
2578
|
} else {
|
|
2505
|
-
await
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2579
|
+
await withSpinner(
|
|
2580
|
+
"Creating variable",
|
|
2581
|
+
() => client.post("/api/variables", {
|
|
2582
|
+
key,
|
|
2583
|
+
value,
|
|
2584
|
+
isSecret: options.secret ?? false,
|
|
2585
|
+
...options.description !== void 0 && { description: options.description }
|
|
2586
|
+
})
|
|
2587
|
+
);
|
|
2511
2588
|
logger.success(`Variable "${key}" created`);
|
|
2512
2589
|
}
|
|
2513
2590
|
});
|
|
2514
2591
|
varCommand.command("delete <key>").description("Delete a variable").option("--force", "Skip confirmation").action(async (key, options) => {
|
|
2515
2592
|
const client = createAuthenticatedClient();
|
|
2516
|
-
const { data: variables } = await
|
|
2593
|
+
const { data: variables } = await withSpinner(
|
|
2594
|
+
"Fetching variables",
|
|
2595
|
+
() => client.get("/api/variables")
|
|
2596
|
+
);
|
|
2517
2597
|
const variable = variables.find((v) => v.key === key);
|
|
2518
2598
|
if (!variable) {
|
|
2519
2599
|
throw new CLIError(`Variable "${key}" not found`, 1 /* GeneralError */);
|
|
@@ -2526,7 +2606,10 @@ varCommand.command("delete <key>").description("Delete a variable").option("--fo
|
|
|
2526
2606
|
return;
|
|
2527
2607
|
}
|
|
2528
2608
|
}
|
|
2529
|
-
await
|
|
2609
|
+
await withSpinner(
|
|
2610
|
+
"Deleting variable",
|
|
2611
|
+
() => client.delete(`/api/variables/${variable.id}`)
|
|
2612
|
+
);
|
|
2530
2613
|
logger.success(`Variable "${key}" deleted`);
|
|
2531
2614
|
});
|
|
2532
2615
|
|
|
@@ -2535,7 +2618,10 @@ import { Command as Command10 } from "commander";
|
|
|
2535
2618
|
var tagCommand = new Command10("tag").description("Manage tags");
|
|
2536
2619
|
tagCommand.command("list").description("List all tags").action(async () => {
|
|
2537
2620
|
const client = createAuthenticatedClient();
|
|
2538
|
-
const { data } = await
|
|
2621
|
+
const { data } = await withSpinner(
|
|
2622
|
+
"Fetching tags",
|
|
2623
|
+
() => client.get("/api/tags")
|
|
2624
|
+
);
|
|
2539
2625
|
output(data, {
|
|
2540
2626
|
columns: [
|
|
2541
2627
|
{ key: "id", header: "ID" },
|
|
@@ -2546,10 +2632,13 @@ tagCommand.command("list").description("List all tags").action(async () => {
|
|
|
2546
2632
|
});
|
|
2547
2633
|
tagCommand.command("create <name>").description("Create a new tag").option("--color <color>", "Tag color (hex)").action(async (name, options) => {
|
|
2548
2634
|
const client = createAuthenticatedClient();
|
|
2549
|
-
const { data } = await
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2635
|
+
const { data } = await withSpinner(
|
|
2636
|
+
"Creating tag",
|
|
2637
|
+
() => client.post("/api/tags", {
|
|
2638
|
+
name,
|
|
2639
|
+
color: options.color
|
|
2640
|
+
})
|
|
2641
|
+
);
|
|
2553
2642
|
logger.success(`Tag "${name}" created (${data.id})`);
|
|
2554
2643
|
});
|
|
2555
2644
|
tagCommand.command("delete <id>").description("Delete a tag").option("--force", "Skip confirmation").action(async (id, options) => {
|
|
@@ -2562,7 +2651,10 @@ tagCommand.command("delete <id>").description("Delete a tag").option("--force",
|
|
|
2562
2651
|
}
|
|
2563
2652
|
}
|
|
2564
2653
|
const client = createAuthenticatedClient();
|
|
2565
|
-
await
|
|
2654
|
+
await withSpinner(
|
|
2655
|
+
"Deleting tag",
|
|
2656
|
+
() => client.delete(`/api/tags/${id}`)
|
|
2657
|
+
);
|
|
2566
2658
|
logger.success(`Tag ${id} deleted`);
|
|
2567
2659
|
});
|
|
2568
2660
|
|
|
@@ -3266,17 +3358,66 @@ function buildStatusPageDefinitions(pages) {
|
|
|
3266
3358
|
}
|
|
3267
3359
|
function generateConfigContent(opts) {
|
|
3268
3360
|
const parts = [];
|
|
3361
|
+
parts.push("/**");
|
|
3362
|
+
parts.push(" * Supercheck Configuration");
|
|
3363
|
+
parts.push(" * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
3364
|
+
parts.push(" *");
|
|
3365
|
+
parts.push(" * This file was generated by `supercheck pull`. It is the source of truth");
|
|
3366
|
+
parts.push(" * for your Supercheck project configuration.");
|
|
3367
|
+
parts.push(" *");
|
|
3368
|
+
parts.push(" * Getting Started:");
|
|
3369
|
+
parts.push(" * 1. Install dependencies:");
|
|
3370
|
+
parts.push(" * npm install -D @supercheck/cli typescript @types/node");
|
|
3371
|
+
parts.push(" * # If using Playwright tests, also install:");
|
|
3372
|
+
parts.push(" * npm install -D @playwright/test");
|
|
3373
|
+
parts.push(" * # If using k6 tests, install k6: https://grafana.com/docs/k6/latest/set-up/install-k6/");
|
|
3374
|
+
parts.push(" *");
|
|
3375
|
+
parts.push(" * 2. Review the configuration below and make any changes you need.");
|
|
3376
|
+
parts.push(" * 3. Preview what will change: npx supercheck diff");
|
|
3377
|
+
parts.push(" * 4. Deploy your changes: npx supercheck deploy");
|
|
3378
|
+
parts.push(" * 5. Re-sync from cloud: npx supercheck pull");
|
|
3379
|
+
parts.push(" *");
|
|
3380
|
+
parts.push(" * CLI Commands:");
|
|
3381
|
+
parts.push(" * supercheck pull Pull remote config & test scripts to local");
|
|
3382
|
+
parts.push(" * supercheck diff Compare local config vs remote");
|
|
3383
|
+
parts.push(" * supercheck deploy Deploy local config to the cloud");
|
|
3384
|
+
parts.push(" * supercheck destroy Remove all managed resources from the cloud");
|
|
3385
|
+
parts.push(" *");
|
|
3386
|
+
parts.push(" * supercheck test list List all tests");
|
|
3387
|
+
parts.push(" * supercheck test validate Validate a local test script");
|
|
3388
|
+
parts.push(" * supercheck test execute <id> Execute a test immediately");
|
|
3389
|
+
parts.push(" *");
|
|
3390
|
+
parts.push(" * supercheck monitor list List all monitors");
|
|
3391
|
+
parts.push(" * supercheck job list List all jobs");
|
|
3392
|
+
parts.push(" * supercheck job run --id <id> Run a job immediately");
|
|
3393
|
+
parts.push(" *");
|
|
3394
|
+
parts.push(" * supercheck config validate Validate this config file");
|
|
3395
|
+
parts.push(" * supercheck health Check API connectivity");
|
|
3396
|
+
parts.push(" * supercheck whoami Show current authentication info");
|
|
3397
|
+
parts.push(" *");
|
|
3398
|
+
parts.push(" * CI/CD Integration:");
|
|
3399
|
+
parts.push(" * Set the SUPERCHECK_TOKEN environment variable in your CI/CD pipeline");
|
|
3400
|
+
parts.push(" * and run `supercheck deploy` to push changes automatically.");
|
|
3401
|
+
parts.push(" *");
|
|
3402
|
+
parts.push(` * Documentation: https://supercheck.io/docs/app/welcome`);
|
|
3403
|
+
parts.push(" */");
|
|
3269
3404
|
parts.push(`import { defineConfig } from '@supercheck/cli'`);
|
|
3270
3405
|
parts.push("");
|
|
3271
3406
|
parts.push("export default defineConfig({");
|
|
3272
3407
|
parts.push(` schemaVersion: '1.0',`);
|
|
3408
|
+
parts.push("");
|
|
3409
|
+
parts.push(" // Project identifiers \u2014 found in Dashboard > Project Settings");
|
|
3273
3410
|
parts.push(" project: {");
|
|
3274
3411
|
parts.push(` organization: '${opts.orgId}',`);
|
|
3275
3412
|
parts.push(` project: '${opts.projectId}',`);
|
|
3276
3413
|
parts.push(" },");
|
|
3414
|
+
parts.push("");
|
|
3415
|
+
parts.push(" // API connection \u2014 set SUPERCHECK_URL env var for self-hosted or staging");
|
|
3277
3416
|
parts.push(" api: {");
|
|
3278
3417
|
parts.push(` baseUrl: process.env.SUPERCHECK_URL ?? '${opts.baseUrl}',`);
|
|
3279
3418
|
parts.push(" },");
|
|
3419
|
+
parts.push("");
|
|
3420
|
+
parts.push(" // Test file patterns \u2014 Playwright (.pw.ts) and k6 (.k6.ts) scripts");
|
|
3280
3421
|
parts.push(" tests: {");
|
|
3281
3422
|
parts.push(" playwright: {");
|
|
3282
3423
|
parts.push(` testMatch: '_supercheck_/tests/**/*.pw.ts',`);
|
|
@@ -3286,42 +3427,33 @@ function generateConfigContent(opts) {
|
|
|
3286
3427
|
parts.push(" },");
|
|
3287
3428
|
parts.push(" },");
|
|
3288
3429
|
if (opts.monitors.length > 0) {
|
|
3430
|
+
parts.push("");
|
|
3431
|
+
parts.push(" // Monitors \u2014 uptime checks, HTTP requests, synthetic tests");
|
|
3289
3432
|
parts.push(` monitors: ${formatArray(opts.monitors, 2)},`);
|
|
3290
3433
|
}
|
|
3291
3434
|
if (opts.jobs.length > 0) {
|
|
3435
|
+
parts.push("");
|
|
3436
|
+
parts.push(" // Jobs \u2014 scheduled or triggered test execution groups");
|
|
3292
3437
|
parts.push(` jobs: ${formatArray(opts.jobs, 2)},`);
|
|
3293
3438
|
}
|
|
3294
3439
|
if (opts.variables.length > 0) {
|
|
3440
|
+
parts.push("");
|
|
3441
|
+
parts.push(" // Variables \u2014 key-value pairs available to tests at runtime");
|
|
3442
|
+
parts.push(" // Secret values use process.env references \u2014 set them in your shell or CI/CD");
|
|
3295
3443
|
parts.push(` variables: ${formatArray(opts.variables, 2)},`);
|
|
3296
3444
|
}
|
|
3297
3445
|
if (opts.tags.length > 0) {
|
|
3446
|
+
parts.push("");
|
|
3447
|
+
parts.push(" // Tags \u2014 labels for organizing tests, monitors, and jobs");
|
|
3298
3448
|
parts.push(` tags: ${formatArray(opts.tags, 2)},`);
|
|
3299
3449
|
}
|
|
3300
3450
|
if (opts.statusPages.length > 0) {
|
|
3451
|
+
parts.push("");
|
|
3452
|
+
parts.push(" // Status Pages \u2014 public dashboards showing monitor health");
|
|
3301
3453
|
parts.push(` statusPages: ${formatArray(opts.statusPages, 2)},`);
|
|
3302
3454
|
}
|
|
3303
3455
|
parts.push("})");
|
|
3304
3456
|
parts.push("");
|
|
3305
|
-
parts.push(`/**`);
|
|
3306
|
-
parts.push(` * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
3307
|
-
parts.push(` * Getting Started`);
|
|
3308
|
-
parts.push(` * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
3309
|
-
parts.push(` *`);
|
|
3310
|
-
parts.push(` * 1. Install dependencies:`);
|
|
3311
|
-
parts.push(` * npm install`);
|
|
3312
|
-
parts.push(` * npx playwright install`);
|
|
3313
|
-
parts.push(` *`);
|
|
3314
|
-
parts.push(` * 2. Run tests:`);
|
|
3315
|
-
parts.push(` * npx supercheck test`);
|
|
3316
|
-
parts.push(` *`);
|
|
3317
|
-
parts.push(` * 3. Useful commands:`);
|
|
3318
|
-
parts.push(` * supercheck diff Preview changes against the cloud`);
|
|
3319
|
-
parts.push(` * supercheck deploy Push local config to Supercheck`);
|
|
3320
|
-
parts.push(` * supercheck pull Sync cloud resources locally`);
|
|
3321
|
-
parts.push(` *`);
|
|
3322
|
-
parts.push(` * Documentation: https://docs.supercheck.io`);
|
|
3323
|
-
parts.push(` */`);
|
|
3324
|
-
parts.push("");
|
|
3325
3457
|
return parts.join("\n");
|
|
3326
3458
|
}
|
|
3327
3459
|
function formatArray(arr, baseIndent) {
|
|
@@ -3476,9 +3608,12 @@ var pullCommand = new Command14("pull").description("Pull tests, monitors, jobs,
|
|
|
3476
3608
|
fullTests.push(t);
|
|
3477
3609
|
} else {
|
|
3478
3610
|
try {
|
|
3479
|
-
const { data } = await
|
|
3480
|
-
|
|
3481
|
-
|
|
3611
|
+
const { data } = await withSpinner(
|
|
3612
|
+
`Fetching script for "${t.title}"`,
|
|
3613
|
+
() => client.get(
|
|
3614
|
+
`/api/tests/${t.id}`,
|
|
3615
|
+
{ includeScript: "true" }
|
|
3616
|
+
)
|
|
3482
3617
|
);
|
|
3483
3618
|
fullTests.push(data);
|
|
3484
3619
|
} catch (err) {
|
|
@@ -3564,41 +3699,12 @@ var pullCommand = new Command14("pull").description("Pull tests, monitors, jobs,
|
|
|
3564
3699
|
}
|
|
3565
3700
|
}
|
|
3566
3701
|
logger.newline();
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
const shouldInstall = await confirmPrompt2("Install dependencies?", { default: true });
|
|
3572
|
-
if (shouldInstall) {
|
|
3573
|
-
const pm = detectPackageManager(cwd);
|
|
3574
|
-
const hasPlaywrightTests = tests.some((t) => t.type !== "performance" && t.type !== "k6");
|
|
3575
|
-
const packages = ["@supercheck/cli", "typescript", "@types/node"];
|
|
3576
|
-
if (hasPlaywrightTests) {
|
|
3577
|
-
packages.push("@playwright/test");
|
|
3578
|
-
}
|
|
3579
|
-
await installDependencies(cwd, pm, {
|
|
3580
|
-
packages,
|
|
3581
|
-
skipInstall: false
|
|
3582
|
-
});
|
|
3583
|
-
}
|
|
3584
|
-
}
|
|
3585
|
-
const hasK6Tests = tests.some((t) => t.type === "performance" || t.type === "k6");
|
|
3586
|
-
if (hasK6Tests) {
|
|
3587
|
-
const hasK6 = await checkK6Binary();
|
|
3588
|
-
if (!hasK6) {
|
|
3589
|
-
logger.warn("k6 binary not found in PATH.");
|
|
3590
|
-
logger.info("Please install k6 to run performance tests: https://grafana.com/docs/k6/latest/set-up/install-k6/");
|
|
3591
|
-
}
|
|
3592
|
-
}
|
|
3593
|
-
if (totalErrors > 0) {
|
|
3594
|
-
throw new CLIError(
|
|
3595
|
-
`Pull completed with ${totalErrors} error(s)`,
|
|
3596
|
-
1 /* GeneralError */
|
|
3597
|
-
);
|
|
3598
|
-
}
|
|
3702
|
+
logger.info("Next steps:");
|
|
3703
|
+
logger.info(" 1. Install dependencies: npm install -D @supercheck/cli typescript");
|
|
3704
|
+
logger.info(" 2. Preview changes: npx supercheck diff");
|
|
3705
|
+
logger.info(" 3. Deploy changes: npx supercheck deploy");
|
|
3599
3706
|
logger.newline();
|
|
3600
3707
|
logger.info("Tip: Review the changes, then commit to version control.");
|
|
3601
|
-
logger.info(" Run `supercheck diff` to compare local vs remote.");
|
|
3602
3708
|
logger.newline();
|
|
3603
3709
|
});
|
|
3604
3710
|
|
|
@@ -3607,7 +3713,10 @@ import { Command as Command15 } from "commander";
|
|
|
3607
3713
|
var notificationCommand = new Command15("notification").alias("notifications").description("Manage notification providers");
|
|
3608
3714
|
notificationCommand.command("list").description("List notification providers").action(async () => {
|
|
3609
3715
|
const client = createAuthenticatedClient();
|
|
3610
|
-
const { data } = await
|
|
3716
|
+
const { data } = await withSpinner(
|
|
3717
|
+
"Fetching notification providers",
|
|
3718
|
+
() => client.get("/api/notification-providers")
|
|
3719
|
+
);
|
|
3611
3720
|
output(data, {
|
|
3612
3721
|
columns: [
|
|
3613
3722
|
{ key: "id", header: "ID" },
|
|
@@ -3620,7 +3729,10 @@ notificationCommand.command("list").description("List notification providers").a
|
|
|
3620
3729
|
});
|
|
3621
3730
|
notificationCommand.command("get <id>").description("Get notification provider details").action(async (id) => {
|
|
3622
3731
|
const client = createAuthenticatedClient();
|
|
3623
|
-
const { data } = await
|
|
3732
|
+
const { data } = await withSpinner(
|
|
3733
|
+
"Fetching provider details",
|
|
3734
|
+
() => client.get(`/api/notification-providers/${id}`)
|
|
3735
|
+
);
|
|
3624
3736
|
outputDetail(data);
|
|
3625
3737
|
});
|
|
3626
3738
|
notificationCommand.command("create").description("Create a notification provider").requiredOption("--type <type>", "Provider type (email, slack, webhook, telegram, discord, teams)").requiredOption("--name <name>", "Provider name").option("--config <json>", "Provider config as JSON string").action(async (options) => {
|
|
@@ -3640,11 +3752,14 @@ notificationCommand.command("create").description("Create a notification provide
|
|
|
3640
3752
|
}
|
|
3641
3753
|
}
|
|
3642
3754
|
const client = createAuthenticatedClient();
|
|
3643
|
-
const { data } = await
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3755
|
+
const { data } = await withSpinner(
|
|
3756
|
+
"Creating notification provider",
|
|
3757
|
+
() => client.post("/api/notification-providers", {
|
|
3758
|
+
name: options.name,
|
|
3759
|
+
type: options.type,
|
|
3760
|
+
config: { name: options.name, ...config }
|
|
3761
|
+
})
|
|
3762
|
+
);
|
|
3648
3763
|
logger.success(`Notification provider "${options.name}" created (${data.id})`);
|
|
3649
3764
|
outputDetail(data);
|
|
3650
3765
|
});
|
|
@@ -3654,7 +3769,10 @@ notificationCommand.command("update <id>").description("Update a notification pr
|
|
|
3654
3769
|
logger.warn("No fields to update. Use --name, --type, or --config.");
|
|
3655
3770
|
return;
|
|
3656
3771
|
}
|
|
3657
|
-
const { data: existing } = await
|
|
3772
|
+
const { data: existing } = await withSpinner(
|
|
3773
|
+
"Fetching existing provider",
|
|
3774
|
+
() => client.get(`/api/notification-providers/${id}`)
|
|
3775
|
+
);
|
|
3658
3776
|
let updatedConfig = existing.config ?? {};
|
|
3659
3777
|
if (options.config) {
|
|
3660
3778
|
try {
|
|
@@ -3671,7 +3789,10 @@ notificationCommand.command("update <id>").description("Update a notification pr
|
|
|
3671
3789
|
type: updatedType,
|
|
3672
3790
|
config: updatedConfig
|
|
3673
3791
|
};
|
|
3674
|
-
const { data } = await
|
|
3792
|
+
const { data } = await withSpinner(
|
|
3793
|
+
"Updating notification provider",
|
|
3794
|
+
() => client.put(`/api/notification-providers/${id}`, body)
|
|
3795
|
+
);
|
|
3675
3796
|
logger.success(`Notification provider ${id} updated`);
|
|
3676
3797
|
outputDetail(data);
|
|
3677
3798
|
});
|
|
@@ -3685,7 +3806,10 @@ notificationCommand.command("delete <id>").description("Delete a notification pr
|
|
|
3685
3806
|
}
|
|
3686
3807
|
}
|
|
3687
3808
|
const client = createAuthenticatedClient();
|
|
3688
|
-
await
|
|
3809
|
+
await withSpinner(
|
|
3810
|
+
"Deleting notification provider",
|
|
3811
|
+
() => client.delete(`/api/notification-providers/${id}`)
|
|
3812
|
+
);
|
|
3689
3813
|
logger.success(`Notification provider ${id} deleted`);
|
|
3690
3814
|
});
|
|
3691
3815
|
notificationCommand.command("test").description("Send a test notification to verify provider configuration").requiredOption("--type <type>", "Provider type (email, slack, webhook, telegram, discord, teams)").requiredOption("--config <json>", "Provider config as JSON string").action(async (options) => {
|
|
@@ -3703,9 +3827,12 @@ notificationCommand.command("test").description("Send a test notification to ver
|
|
|
3703
3827
|
throw new CLIError("Invalid JSON in --config", 1 /* GeneralError */);
|
|
3704
3828
|
}
|
|
3705
3829
|
const client = createAuthenticatedClient();
|
|
3706
|
-
const { data } = await
|
|
3707
|
-
"
|
|
3708
|
-
|
|
3830
|
+
const { data } = await withSpinner(
|
|
3831
|
+
"Sending test notification",
|
|
3832
|
+
() => client.post(
|
|
3833
|
+
"/api/notification-providers/test",
|
|
3834
|
+
{ type: options.type, config }
|
|
3835
|
+
)
|
|
3709
3836
|
);
|
|
3710
3837
|
if (data.success) {
|
|
3711
3838
|
logger.success(data.message ?? "Test notification sent successfully");
|
|
@@ -3719,7 +3846,10 @@ import { Command as Command16 } from "commander";
|
|
|
3719
3846
|
var alertCommand = new Command16("alert").alias("alerts").description("View alert history");
|
|
3720
3847
|
alertCommand.command("history").description("Get alert history").option("--page <page>", "Page number", "1").option("--limit <limit>", "Number of results per page", "50").action(async (options) => {
|
|
3721
3848
|
const client = createAuthenticatedClient();
|
|
3722
|
-
const { data } = await
|
|
3849
|
+
const { data } = await withSpinner(
|
|
3850
|
+
"Fetching alert history",
|
|
3851
|
+
() => client.get("/api/alerts/history", { page: options.page, limit: options.limit })
|
|
3852
|
+
);
|
|
3723
3853
|
const items = Array.isArray(data) ? data : data.data;
|
|
3724
3854
|
const pagination = Array.isArray(data) ? null : data.pagination;
|
|
3725
3855
|
output(items, {
|
|
@@ -3750,7 +3880,10 @@ var auditCommand = new Command17("audit").description("View audit logs (admin)")
|
|
|
3750
3880
|
};
|
|
3751
3881
|
if (options.search) params.search = options.search;
|
|
3752
3882
|
if (options.action) params.action = options.action;
|
|
3753
|
-
const { data } = await
|
|
3883
|
+
const { data } = await withSpinner(
|
|
3884
|
+
"Fetching audit logs",
|
|
3885
|
+
() => client.get("/api/audit", params)
|
|
3886
|
+
);
|
|
3754
3887
|
if (!data.success) {
|
|
3755
3888
|
throw new CLIError("Failed to fetch audit logs", 4 /* ApiError */);
|
|
3756
3889
|
}
|