@smooai/testing 1.0.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +94 -26
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -1
- package/dist/lib/types.d.mts +4 -0
- package/dist/lib/types.d.ts +4 -0
- package/dist/lib/types.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -55,7 +55,7 @@ var init_api_client = __esm({
|
|
|
55
55
|
"src/cli/utils/api-client.ts"() {
|
|
56
56
|
"use strict";
|
|
57
57
|
init_auth();
|
|
58
|
-
ApiClient = class {
|
|
58
|
+
ApiClient = class _ApiClient {
|
|
59
59
|
credentials;
|
|
60
60
|
baseUrl;
|
|
61
61
|
constructor(credentials) {
|
|
@@ -108,6 +108,13 @@ var init_api_client = __esm({
|
|
|
108
108
|
async delete(path) {
|
|
109
109
|
return this.request("DELETE", this.orgPath(path));
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Create a new ApiClient with the same auth credentials but a different org ID.
|
|
113
|
+
* Useful for pushing results to multiple organizations with cross-org M2M tokens.
|
|
114
|
+
*/
|
|
115
|
+
withOrgId(orgId) {
|
|
116
|
+
return new _ApiClient({ ...this.credentials, orgId });
|
|
117
|
+
}
|
|
111
118
|
};
|
|
112
119
|
}
|
|
113
120
|
});
|
|
@@ -692,6 +699,7 @@ async function runCreate4(options) {
|
|
|
692
699
|
if (options.environmentId) body.environmentId = options.environmentId;
|
|
693
700
|
if (options.deploymentId) body.deploymentId = options.deploymentId;
|
|
694
701
|
if (options.tool) body.tool = options.tool;
|
|
702
|
+
if (options.tags) body.tags = options.tags.split(",").map((t) => t.trim());
|
|
695
703
|
if (options.runnerName) body.runnerName = options.runnerName;
|
|
696
704
|
if (options.runnerUrl) body.runnerUrl = options.runnerUrl;
|
|
697
705
|
const run = await client.post("/testing/runs", body);
|
|
@@ -727,6 +735,7 @@ async function runList4(options) {
|
|
|
727
735
|
status: options.status,
|
|
728
736
|
environmentId: options.environmentId,
|
|
729
737
|
tool: options.tool,
|
|
738
|
+
tags: options.tags,
|
|
730
739
|
limit: options.limit,
|
|
731
740
|
offset: options.offset
|
|
732
741
|
});
|
|
@@ -966,11 +975,7 @@ import { basename } from "path";
|
|
|
966
975
|
import { render, Box as Box3, Text as Text3 } from "ink";
|
|
967
976
|
import { useEffect, useState } from "react";
|
|
968
977
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
969
|
-
|
|
970
|
-
const report = parseCtrfFile(ctrfFile);
|
|
971
|
-
const summary = summarizeCtrfResults(report);
|
|
972
|
-
const creds = getCredentialsOrExit();
|
|
973
|
-
const client = new ApiClient(creds);
|
|
978
|
+
function buildRunBody(ctrfFile, report, options) {
|
|
974
979
|
const runName = options.name ?? basename(ctrfFile, ".json");
|
|
975
980
|
const runBody = {
|
|
976
981
|
name: runName,
|
|
@@ -979,11 +984,15 @@ async function reportLogic(ctrfFile, options) {
|
|
|
979
984
|
};
|
|
980
985
|
if (options.environment) runBody.environment = options.environment;
|
|
981
986
|
if (options.deploymentId) runBody.deploymentId = options.deploymentId;
|
|
987
|
+
if (options.tags) runBody.tags = options.tags.split(",").map((t) => t.trim());
|
|
982
988
|
if (options.buildUrl) {
|
|
983
989
|
runBody.buildUrl = options.buildUrl;
|
|
984
990
|
} else if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) {
|
|
985
991
|
runBody.buildUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
|
|
986
992
|
}
|
|
993
|
+
return runBody;
|
|
994
|
+
}
|
|
995
|
+
async function submitReportToOrg(client, report, runBody) {
|
|
987
996
|
const run = await client.post("/testing/runs", runBody);
|
|
988
997
|
let resultCount = 0;
|
|
989
998
|
try {
|
|
@@ -999,16 +1008,47 @@ async function reportLogic(ctrfFile, options) {
|
|
|
999
1008
|
throw err;
|
|
1000
1009
|
}
|
|
1001
1010
|
const updatedRun = await client.get(`/testing/runs/${run.id}`);
|
|
1002
|
-
return { run: updatedRun, resultCount
|
|
1011
|
+
return { run: updatedRun, resultCount };
|
|
1012
|
+
}
|
|
1013
|
+
function parseAdditionalOrgIds(raw) {
|
|
1014
|
+
if (!raw) return [];
|
|
1015
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1016
|
+
}
|
|
1017
|
+
async function reportLogic(ctrfFile, options) {
|
|
1018
|
+
const report = parseCtrfFile(ctrfFile);
|
|
1019
|
+
const summary = summarizeCtrfResults(report);
|
|
1020
|
+
const creds = getCredentialsOrExit();
|
|
1021
|
+
const client = new ApiClient(creds);
|
|
1022
|
+
const runBody = buildRunBody(ctrfFile, report, options);
|
|
1023
|
+
const { run, resultCount } = await submitReportToOrg(client, report, runBody);
|
|
1024
|
+
const additionalOrgIds = parseAdditionalOrgIds(options.additionalOrgIds);
|
|
1025
|
+
const additionalResults = [];
|
|
1026
|
+
for (const orgId of additionalOrgIds) {
|
|
1027
|
+
try {
|
|
1028
|
+
const altClient = client.withOrgId(orgId);
|
|
1029
|
+
const result = await submitReportToOrg(altClient, report, runBody);
|
|
1030
|
+
additionalResults.push({ orgId, run: result.run, resultCount: result.resultCount });
|
|
1031
|
+
} catch (err) {
|
|
1032
|
+
additionalResults.push({ orgId, error: err instanceof Error ? err.message : String(err) });
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
return { run, resultCount, summary, additionalResults };
|
|
1003
1036
|
}
|
|
1004
1037
|
function ReportUI({ ctrfFile, options }) {
|
|
1038
|
+
const additionalOrgIds = parseAdditionalOrgIds(options.additionalOrgIds);
|
|
1039
|
+
const additionalOrgTasks = additionalOrgIds.map((orgId) => ({
|
|
1040
|
+
label: `Pushing results to org ${orgId}`,
|
|
1041
|
+
status: "pending"
|
|
1042
|
+
}));
|
|
1005
1043
|
const [tasks, setTasks] = useState([
|
|
1006
1044
|
{ label: "Parsing CTRF report", status: "pending" },
|
|
1007
1045
|
{ label: "Authenticating", status: "pending" },
|
|
1008
1046
|
{ label: "Creating test run", status: "pending" },
|
|
1009
|
-
{ label: "Submitting results", status: "pending" }
|
|
1047
|
+
{ label: "Submitting results", status: "pending" },
|
|
1048
|
+
...additionalOrgTasks
|
|
1010
1049
|
]);
|
|
1011
1050
|
const [result, setResult] = useState(null);
|
|
1051
|
+
const additionalStartIndex = 4;
|
|
1012
1052
|
useEffect(() => {
|
|
1013
1053
|
(async () => {
|
|
1014
1054
|
try {
|
|
@@ -1020,20 +1060,8 @@ function ReportUI({ ctrfFile, options }) {
|
|
|
1020
1060
|
const creds = getCredentialsOrExit();
|
|
1021
1061
|
const client = new ApiClient(creds);
|
|
1022
1062
|
setTasks((t) => t.map((task, i) => i === 1 ? { ...task, status: "done" } : task));
|
|
1063
|
+
const runBody = buildRunBody(ctrfFile, report, options);
|
|
1023
1064
|
setTasks((t) => t.map((task, i) => i === 2 ? { ...task, status: "running" } : task));
|
|
1024
|
-
const runName = options.name ?? basename(ctrfFile, ".json");
|
|
1025
|
-
const runBody = {
|
|
1026
|
-
name: runName,
|
|
1027
|
-
tool: options.tool ?? report.results.tool?.name,
|
|
1028
|
-
buildName: options.buildName ?? process.env.GITHUB_SHA
|
|
1029
|
-
};
|
|
1030
|
-
if (options.environment) runBody.environment = options.environment;
|
|
1031
|
-
if (options.deploymentId) runBody.deploymentId = options.deploymentId;
|
|
1032
|
-
if (options.buildUrl) {
|
|
1033
|
-
runBody.buildUrl = options.buildUrl;
|
|
1034
|
-
} else if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) {
|
|
1035
|
-
runBody.buildUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
|
|
1036
|
-
}
|
|
1037
1065
|
const run = await client.post("/testing/runs", runBody);
|
|
1038
1066
|
setTasks((t) => t.map((task, i) => i === 2 ? { ...task, status: "done" } : task));
|
|
1039
1067
|
setTasks((t) => t.map((task, i) => i === 3 ? { ...task, status: "running" } : task));
|
|
@@ -1042,7 +1070,23 @@ function ReportUI({ ctrfFile, options }) {
|
|
|
1042
1070
|
});
|
|
1043
1071
|
setTasks((t) => t.map((task, i) => i === 3 ? { ...task, status: "done" } : task));
|
|
1044
1072
|
const updatedRun = await client.get(`/testing/runs/${run.id}`);
|
|
1045
|
-
|
|
1073
|
+
const additionalResults = [];
|
|
1074
|
+
for (let idx = 0; idx < additionalOrgIds.length; idx++) {
|
|
1075
|
+
const orgId = additionalOrgIds[idx];
|
|
1076
|
+
const taskIdx = additionalStartIndex + idx;
|
|
1077
|
+
setTasks((t) => t.map((task, i) => i === taskIdx ? { ...task, status: "running" } : task));
|
|
1078
|
+
try {
|
|
1079
|
+
const altClient = client.withOrgId(orgId);
|
|
1080
|
+
const altResult = await submitReportToOrg(altClient, report, runBody);
|
|
1081
|
+
additionalResults.push({ orgId, run: altResult.run, resultCount: altResult.resultCount });
|
|
1082
|
+
setTasks((t) => t.map((task, i) => i === taskIdx ? { ...task, status: "done" } : task));
|
|
1083
|
+
} catch (err) {
|
|
1084
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1085
|
+
additionalResults.push({ orgId, error: errorMsg });
|
|
1086
|
+
setTasks((t) => t.map((task, i) => i === taskIdx ? { ...task, status: "error", error: errorMsg } : task));
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
setResult({ run: updatedRun, resultCount: resultResponse.count, summary, additionalResults });
|
|
1046
1090
|
} catch (err) {
|
|
1047
1091
|
setTasks(
|
|
1048
1092
|
(t) => t.map((task) => task.status === "running" ? { ...task, status: "error", error: err instanceof Error ? err.message : String(err) } : task)
|
|
@@ -1071,6 +1115,27 @@ function ReportUI({ ctrfFile, options }) {
|
|
|
1071
1115
|
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1072
1116
|
"Run ID: ",
|
|
1073
1117
|
result.run.id
|
|
1118
|
+
] }),
|
|
1119
|
+
result.additionalResults.length > 0 && /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
1120
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, children: "Additional orgs:" }),
|
|
1121
|
+
result.additionalResults.map(
|
|
1122
|
+
(ar) => "error" in ar ? /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
|
|
1123
|
+
" ",
|
|
1124
|
+
"\u2717 ",
|
|
1125
|
+
ar.orgId,
|
|
1126
|
+
": ",
|
|
1127
|
+
ar.error
|
|
1128
|
+
] }, ar.orgId) : /* @__PURE__ */ jsxs3(Text3, { color: "green", children: [
|
|
1129
|
+
" ",
|
|
1130
|
+
"\u2713 ",
|
|
1131
|
+
ar.orgId,
|
|
1132
|
+
": Run ",
|
|
1133
|
+
ar.run.id,
|
|
1134
|
+
" \u2014 ",
|
|
1135
|
+
ar.resultCount,
|
|
1136
|
+
" results"
|
|
1137
|
+
] }, ar.orgId)
|
|
1138
|
+
)
|
|
1074
1139
|
] })
|
|
1075
1140
|
] })
|
|
1076
1141
|
] });
|
|
@@ -1083,7 +1148,10 @@ function runReport(ctrfFile, options) {
|
|
|
1083
1148
|
runId: result.run.id,
|
|
1084
1149
|
status: result.run.status,
|
|
1085
1150
|
resultCount: result.resultCount,
|
|
1086
|
-
summary: result.summary
|
|
1151
|
+
summary: result.summary,
|
|
1152
|
+
additionalResults: result.additionalResults.map(
|
|
1153
|
+
(ar) => "error" in ar ? { orgId: ar.orgId, success: false, error: ar.error } : { orgId: ar.orgId, success: true, runId: ar.run.id, resultCount: ar.resultCount }
|
|
1154
|
+
)
|
|
1087
1155
|
}),
|
|
1088
1156
|
(err) => {
|
|
1089
1157
|
errorOutput(err instanceof Error ? err.message : String(err));
|
|
@@ -1318,11 +1386,11 @@ function createEnvironmentsCommand(program2) {
|
|
|
1318
1386
|
// src/cli/commands/runs/index.ts
|
|
1319
1387
|
function createRunsCommand(program2) {
|
|
1320
1388
|
const runs = program2.command("runs").description("Manage test runs");
|
|
1321
|
-
runs.command("create").description("Create a test run").requiredOption("--name <name>", "Run name").option("--environment <name>", "Environment name (auto-creates if needed)").option("--environment-id <id>", "Environment ID").option("--deployment-id <id>", "Deployment ID").option("--tool <name>", "Tool name (e.g., vitest, playwright)").option("--runner-name <name>", "Runner name").option("--runner-url <url>", "Runner URL").action(async (opts) => {
|
|
1389
|
+
runs.command("create").description("Create a test run").requiredOption("--name <name>", "Run name").option("--environment <name>", "Environment name (auto-creates if needed)").option("--environment-id <id>", "Environment ID").option("--deployment-id <id>", "Deployment ID").option("--tool <name>", "Tool name (e.g., vitest, playwright)").option("--tags <tags>", "Comma-separated tags (e.g., e2e,brent-rager)").option("--runner-name <name>", "Runner name").option("--runner-url <url>", "Runner URL").action(async (opts) => {
|
|
1322
1390
|
const { runCreate: runCreate5 } = await Promise.resolve().then(() => (init_create4(), create_exports4));
|
|
1323
1391
|
runCreate5({ ...opts, json: program2.opts().json ?? opts.json });
|
|
1324
1392
|
});
|
|
1325
|
-
runs.command("list").description("List test runs").option("--status <status>", "Filter by status").option("--environment-id <id>", "Filter by environment ID").option("--tool <name>", "Filter by tool").option("--limit <n>", "Max results", "50").option("--offset <n>", "Offset", "0").action(async (opts) => {
|
|
1393
|
+
runs.command("list").description("List test runs").option("--status <status>", "Filter by status").option("--environment-id <id>", "Filter by environment ID").option("--tool <name>", "Filter by tool").option("--tags <tags>", "Filter by tags (comma-separated)").option("--limit <n>", "Max results", "50").option("--offset <n>", "Offset", "0").action(async (opts) => {
|
|
1326
1394
|
const { runList: runList5 } = await Promise.resolve().then(() => (init_list4(), list_exports4));
|
|
1327
1395
|
runList5({ ...opts, json: program2.opts().json ?? opts.json });
|
|
1328
1396
|
});
|
|
@@ -1334,7 +1402,7 @@ function createRunsCommand(program2) {
|
|
|
1334
1402
|
const { runUpdate: runUpdate5 } = await Promise.resolve().then(() => (init_update4(), update_exports4));
|
|
1335
1403
|
runUpdate5(id, { ...opts, json: program2.opts().json ?? opts.json });
|
|
1336
1404
|
});
|
|
1337
|
-
runs.command("report").description("Report test results from a CTRF file (create run + submit results)").argument("<ctrf-file>", "Path to CTRF JSON report file").option("--name <name>", "Run name (defaults to filename)").option("--environment <name>", "Environment name").option("--deployment-id <id>", "Deployment ID").option("--tool <name>", "Override tool name from CTRF").option("--build-name <name>", "Build name (e.g., git SHA)").option("--build-url <url>", "Build URL (e.g., CI run link)").action(async (ctrfFile, opts) => {
|
|
1405
|
+
runs.command("report").description("Report test results from a CTRF file (create run + submit results)").argument("<ctrf-file>", "Path to CTRF JSON report file").option("--name <name>", "Run name (defaults to filename)").option("--environment <name>", "Environment name").option("--deployment-id <id>", "Deployment ID").option("--tool <name>", "Override tool name from CTRF").option("--tags <tags>", "Comma-separated tags (e.g., e2e,brent-rager)").option("--build-name <name>", "Build name (e.g., git SHA)").option("--build-url <url>", "Build URL (e.g., CI run link)").option("--additional-org-ids <ids>", "Comma-separated org IDs to also push results to").action(async (ctrfFile, opts) => {
|
|
1338
1406
|
const { runReport: runReport2 } = await Promise.resolve().then(() => (init_report(), report_exports));
|
|
1339
1407
|
runReport2(ctrfFile, { ...opts, json: program2.opts().json ?? opts.json });
|
|
1340
1408
|
});
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/utils/auth.ts","../src/cli/utils/api-client.ts","../src/cli/utils/credentials.ts","../src/cli/utils/output.ts","../src/cli/commands/cases/create.ts","../src/cli/commands/cases/list.ts","../src/cli/commands/cases/get.ts","../src/cli/commands/cases/update.ts","../src/cli/commands/cases/delete.ts","../src/cli/commands/deployments/create.ts","../src/cli/commands/deployments/list.ts","../src/cli/commands/deployments/get.ts","../src/cli/commands/deployments/update.ts","../src/cli/commands/deployments/delete.ts","../src/cli/commands/environments/create.ts","../src/cli/commands/environments/list.ts","../src/cli/commands/environments/get.ts","../src/cli/commands/environments/update.ts","../src/cli/commands/runs/create.ts","../src/cli/commands/runs/list.ts","../src/cli/commands/runs/get.ts","../src/cli/commands/runs/update.ts","../src/cli/components/Banner.tsx","../src/cli/components/TaskList.tsx","../src/cli/utils/ctrf.ts","../src/cli/commands/runs/report.tsx","../src/cli/commands/auth/login.tsx","../src/cli/commands/auth/logout.ts","../src/cli/commands/auth/status.ts","../src/cli/index.ts","../src/cli/commands/cases/index.ts","../src/cli/commands/deployments/index.ts","../src/cli/commands/environments/index.ts","../src/cli/commands/runs/index.ts"],"sourcesContent":["/**\n * M2M token exchange for CLI authentication.\n * Authenticates via client_credentials grant type against the Smoo AI auth service.\n */\n\nimport type { Credentials, TokenResponse } from '../../lib/types';\n\nlet cachedToken: string | null = null;\nlet tokenExpiresAt = 0;\n\nexport async function getAuthToken(credentials: Credentials): Promise<string> {\n // Return cached token if still valid (with 60s buffer)\n if (cachedToken && Date.now() < tokenExpiresAt - 60_000) {\n return cachedToken;\n }\n\n const response = await fetch(credentials.authUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'client_credentials',\n provider: 'client_credentials',\n client_id: credentials.clientId,\n client_secret: credentials.clientSecret,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n throw new Error(`Authentication failed: HTTP ${response.status}${body ? ` — ${body}` : ''}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Authentication failed: no access_token in response');\n }\n\n cachedToken = data.access_token;\n // Default to 1h TTL if not specified\n tokenExpiresAt = Date.now() + (data.expires_in || 3600) * 1000;\n\n return cachedToken;\n}\n\nexport function clearTokenCache(): void {\n cachedToken = null;\n tokenExpiresAt = 0;\n}\n","/**\n * HTTP client wrapper with M2M auth for the Smoo AI Testing API.\n */\n\nimport type { Credentials } from '../../lib/types';\nimport { clearTokenCache, getAuthToken } from './auth';\n\nexport class ApiClient {\n private credentials: Credentials;\n private baseUrl: string;\n\n constructor(credentials: Credentials) {\n this.credentials = credentials;\n this.baseUrl = credentials.apiUrl.replace(/\\/+$/, '');\n }\n\n private orgPath(path: string): string {\n return `${this.baseUrl}/organizations/${this.credentials.orgId}${path}`;\n }\n\n private async request<T>(method: string, url: string, body?: unknown, retry = true): Promise<T> {\n const token = await getAuthToken(this.credentials);\n\n const response = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: body != null ? JSON.stringify(body) : undefined,\n });\n\n // Retry once on 401 (token may have expired)\n if (response.status === 401 && retry) {\n clearTokenCache();\n return this.request<T>(method, url, body, false);\n }\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new Error(`API error: HTTP ${response.status} ${response.statusText}${errorBody ? ` — ${errorBody}` : ''}`);\n }\n\n const text = await response.text();\n if (!text) return undefined as T;\n return JSON.parse(text) as T;\n }\n\n async get<T>(path: string, params?: Record<string, string | number | undefined>): Promise<T> {\n let url = this.orgPath(path);\n if (params) {\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value != null) searchParams.set(key, String(value));\n }\n const qs = searchParams.toString();\n if (qs) url += `?${qs}`;\n }\n return this.request<T>('GET', url);\n }\n\n async post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('POST', this.orgPath(path), body);\n }\n\n async patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PATCH', this.orgPath(path), body);\n }\n\n async delete<T>(path: string): Promise<T> {\n return this.request<T>('DELETE', this.orgPath(path));\n }\n}\n","/**\n * Manage CLI credentials stored at ~/.smooai/credentials.json.\n * Shared with @smooai/config — same file, extended fields for M2M auth.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport type { Credentials } from '../../lib/types';\n\nconst SMOOAI_DIR = join(homedir(), '.smooai');\nconst CREDENTIALS_FILE = join(SMOOAI_DIR, 'credentials.json');\n\nconst DEFAULT_API_URL = 'https://api.production.smoo.ai';\nconst DEFAULT_AUTH_URL = 'https://auth.production.smoo.ai/token';\n\nexport function loadCredentials(): Credentials | null {\n // Try env vars first (CI environments)\n const envClientId = process.env.SMOOAI_CLIENT_ID;\n const envClientSecret = process.env.SMOOAI_CLIENT_SECRET;\n const envOrgId = process.env.SMOOAI_ORG_ID;\n\n if (envClientId && envClientSecret && envOrgId) {\n return {\n clientId: envClientId,\n clientSecret: envClientSecret,\n orgId: envOrgId,\n apiUrl: process.env.SMOOAI_API_URL || DEFAULT_API_URL,\n authUrl: process.env.SMOOAI_AUTH_URL || DEFAULT_AUTH_URL,\n };\n }\n\n // Fall back to credentials file\n try {\n if (!existsSync(CREDENTIALS_FILE)) return null;\n const raw = readFileSync(CREDENTIALS_FILE, 'utf-8');\n const parsed = JSON.parse(raw);\n if (!parsed.clientId || !parsed.clientSecret || !parsed.orgId) return null;\n return {\n clientId: parsed.clientId,\n clientSecret: parsed.clientSecret,\n orgId: parsed.orgId,\n apiUrl: parsed.apiUrl || DEFAULT_API_URL,\n authUrl: parsed.authUrl || DEFAULT_AUTH_URL,\n };\n } catch {\n return null;\n }\n}\n\nexport function saveCredentials(credentials: Credentials): void {\n mkdirSync(SMOOAI_DIR, { recursive: true });\n\n // Merge with existing credentials to preserve other SDK fields\n let existing: Record<string, unknown> = {};\n try {\n if (existsSync(CREDENTIALS_FILE)) {\n existing = JSON.parse(readFileSync(CREDENTIALS_FILE, 'utf-8'));\n }\n } catch {\n // ignore\n }\n\n const merged = { ...existing, ...credentials };\n writeFileSync(CREDENTIALS_FILE, JSON.stringify(merged, null, 2), { mode: 0o600 });\n}\n\nexport function clearCredentials(): void {\n if (!existsSync(CREDENTIALS_FILE)) return;\n\n try {\n const existing = JSON.parse(readFileSync(CREDENTIALS_FILE, 'utf-8'));\n // Remove only testing SDK fields, preserve @smooai/config fields\n delete existing.clientId;\n delete existing.clientSecret;\n // Keep orgId, apiUrl, authUrl as they may be shared\n writeFileSync(CREDENTIALS_FILE, JSON.stringify(existing, null, 2), { mode: 0o600 });\n } catch {\n // ignore\n }\n}\n\nexport function getCredentialsOrExit(): Credentials {\n const creds = loadCredentials();\n if (!creds) {\n console.error('Not logged in. Run `smooai-testing login` first, or set SMOOAI_CLIENT_ID, SMOOAI_CLIENT_SECRET, and SMOOAI_ORG_ID env vars.');\n process.exit(1);\n }\n return creds;\n}\n","/**\n * Dual-mode output utilities for CLI commands.\n * Supports both interactive (Ink TUI) and non-interactive (JSON) output.\n */\n\nexport function isInteractive(jsonFlag?: boolean): boolean {\n if (jsonFlag) return false;\n return Boolean(process.stdout.isTTY);\n}\n\nexport function jsonOutput(data: unknown, exitCode = 0): never {\n console.log(JSON.stringify(data, null, 2));\n process.exit(exitCode);\n}\n\nexport function errorOutput(message: string, details?: unknown): never {\n if (isInteractive()) {\n console.error(`Error: ${message}`);\n if (details) console.error(details);\n process.exit(1);\n }\n jsonOutput({ success: false, error: message, details }, 1);\n}\n","import type { TestCase } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface CreateOptions {\n json?: boolean;\n title: string;\n description?: string;\n priority?: string;\n automationStatus?: string;\n tags?: string;\n}\n\nexport async function runCreate(options: CreateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = { title: options.title };\n if (options.description) body.description = options.description;\n if (options.priority) body.priority = options.priority;\n if (options.automationStatus) body.automationStatus = options.automationStatus;\n if (options.tags) body.tags = options.tags.split(',').map((t) => t.trim());\n\n const tc = await client.post<TestCase>('/testing/cases', body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(tc);\n }\n\n console.log(`Created test case: ${tc.id}`);\n console.log(` Title: ${tc.title}`);\n console.log(` Priority: ${tc.priority ?? 'N/A'}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { PaginatedResponse, TestCase } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface ListOptions {\n json?: boolean;\n tags?: string;\n priority?: string;\n automationStatus?: string;\n limit?: string;\n offset?: string;\n}\n\nexport async function runList(options: ListOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const result = await client.get<PaginatedResponse<TestCase>>('/testing/cases', {\n tags: options.tags,\n priority: options.priority,\n automationStatus: options.automationStatus,\n limit: options.limit,\n offset: options.offset,\n });\n\n if (!isInteractive(options.json)) {\n jsonOutput(result);\n }\n\n console.log(`Test Cases (${result.data.length} of ${result.pagination.total}):\\n`);\n for (const tc of result.data) {\n const tags = tc.tags?.length ? ` [${tc.tags.join(', ')}]` : '';\n console.log(` ${tc.id} ${(tc.priority ?? '-').padEnd(8)} ${tc.title}${tags}`);\n }\n\n if (result.pagination.hasMore) {\n console.log(`\\n ... ${result.pagination.total - result.data.length} more. Use --offset to paginate.`);\n }\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestCase } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface GetOptions {\n json?: boolean;\n}\n\nexport async function runGet(id: string, options: GetOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const tc = await client.get<TestCase>(`/testing/cases/${id}`);\n\n if (!isInteractive(options.json)) {\n jsonOutput(tc);\n }\n\n console.log(`Test Case: ${tc.id}`);\n console.log(` Title: ${tc.title}`);\n console.log(` Priority: ${tc.priority ?? 'N/A'}`);\n console.log(` Automation: ${tc.automationStatus ?? 'N/A'}`);\n if (tc.description) console.log(` Description: ${tc.description}`);\n if (tc.tags?.length) console.log(` Tags: ${tc.tags.join(', ')}`);\n console.log(` Created: ${tc.createdAt}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestCase } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface UpdateOptions {\n json?: boolean;\n title?: string;\n description?: string;\n priority?: string;\n automationStatus?: string;\n tags?: string;\n}\n\nexport async function runUpdate(id: string, options: UpdateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = {};\n if (options.title) body.title = options.title;\n if (options.description) body.description = options.description;\n if (options.priority) body.priority = options.priority;\n if (options.automationStatus) body.automationStatus = options.automationStatus;\n if (options.tags) body.tags = options.tags.split(',').map((t) => t.trim());\n\n const tc = await client.patch<TestCase>(`/testing/cases/${id}`, body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(tc);\n }\n\n console.log(`Updated test case: ${tc.id}`);\n console.log(` Title: ${tc.title}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface DeleteOptions {\n json?: boolean;\n}\n\nexport async function runDelete(id: string, options: DeleteOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n await client.delete(`/testing/cases/${id}`);\n\n if (!isInteractive(options.json)) {\n jsonOutput({ success: true, id });\n }\n\n console.log(`Deleted test case: ${id}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { Deployment } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface CreateOptions {\n json?: boolean;\n name: string;\n environmentId?: string;\n status?: string;\n source?: string;\n externalId?: string;\n externalUrl?: string;\n ref?: string;\n}\n\nexport async function runCreate(options: CreateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = { name: options.name };\n if (options.environmentId) body.environmentId = options.environmentId;\n if (options.status) body.status = options.status;\n if (options.source) body.source = options.source;\n if (options.externalId) body.externalId = options.externalId;\n if (options.externalUrl) body.externalUrl = options.externalUrl;\n if (options.ref) body.ref = options.ref;\n\n const deployment = await client.post<Deployment>('/testing/deployments', body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(deployment);\n }\n\n console.log(`Created deployment: ${deployment.id}`);\n console.log(` Name: ${deployment.name}`);\n console.log(` Status: ${deployment.status}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { Deployment, PaginatedResponse } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface ListOptions {\n json?: boolean;\n status?: string;\n environmentId?: string;\n source?: string;\n limit?: string;\n offset?: string;\n}\n\nexport async function runList(options: ListOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const result = await client.get<PaginatedResponse<Deployment>>('/testing/deployments', {\n status: options.status,\n environmentId: options.environmentId,\n source: options.source,\n limit: options.limit,\n offset: options.offset,\n });\n\n if (!isInteractive(options.json)) {\n jsonOutput(result);\n }\n\n console.log(`Deployments (${result.data.length} of ${result.pagination.total}):\\n`);\n for (const d of result.data) {\n const ref = d.ref ? ` (${d.ref})` : '';\n console.log(` ${d.id} ${d.status.padEnd(12)} ${d.name}${ref}`);\n }\n\n if (result.pagination.hasMore) {\n console.log(`\\n ... ${result.pagination.total - result.data.length} more. Use --offset to paginate.`);\n }\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { Deployment } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface GetOptions {\n json?: boolean;\n}\n\nexport async function runGet(id: string, options: GetOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const deployment = await client.get<Deployment>(`/testing/deployments/${id}`);\n\n if (!isInteractive(options.json)) {\n jsonOutput(deployment);\n }\n\n console.log(`Deployment: ${deployment.id}`);\n console.log(` Name: ${deployment.name}`);\n console.log(` Status: ${deployment.status}`);\n console.log(` Source: ${deployment.source ?? 'N/A'}`);\n console.log(` Ref: ${deployment.ref ?? 'N/A'}`);\n console.log(` External URL: ${deployment.externalUrl ?? 'N/A'}`);\n console.log(` Created: ${deployment.createdAt}`);\n if (deployment.completedAt) {\n console.log(` Completed: ${deployment.completedAt}`);\n }\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { Deployment } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface UpdateOptions {\n json?: boolean;\n name?: string;\n status?: string;\n externalUrl?: string;\n}\n\nexport async function runUpdate(id: string, options: UpdateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = {};\n if (options.name) body.name = options.name;\n if (options.status) body.status = options.status;\n if (options.externalUrl) body.externalUrl = options.externalUrl;\n\n const deployment = await client.patch<Deployment>(`/testing/deployments/${id}`, body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(deployment);\n }\n\n console.log(`Updated deployment: ${deployment.id}`);\n console.log(` Status: ${deployment.status}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface DeleteOptions {\n json?: boolean;\n}\n\nexport async function runDelete(id: string, options: DeleteOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n await client.delete(`/testing/deployments/${id}`);\n\n if (!isInteractive(options.json)) {\n jsonOutput({ success: true, id });\n }\n\n console.log(`Deleted deployment: ${id}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestEnvironment } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface CreateOptions {\n json?: boolean;\n name: string;\n description?: string;\n baseUrl?: string;\n}\n\nexport async function runCreate(options: CreateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = { name: options.name };\n if (options.description) body.description = options.description;\n if (options.baseUrl) body.baseUrl = options.baseUrl;\n\n const env = await client.post<TestEnvironment>('/testing/environments', body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(env);\n }\n\n console.log(`Created environment: ${env.id}`);\n console.log(` Name: ${env.name}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestEnvironment } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface ListOptions {\n json?: boolean;\n}\n\nexport async function runList(options: ListOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const envs = await client.get<TestEnvironment[]>('/testing/environments');\n\n if (!isInteractive(options.json)) {\n jsonOutput(envs);\n }\n\n console.log(`Test Environments (${envs.length}):\\n`);\n for (const env of envs) {\n const url = env.baseUrl ? ` (${env.baseUrl})` : '';\n console.log(` ${env.id} ${env.name}${url}`);\n }\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestEnvironment } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface GetOptions {\n json?: boolean;\n}\n\nexport async function runGet(id: string, options: GetOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const env = await client.get<TestEnvironment>(`/testing/environments/${id}`);\n\n if (!isInteractive(options.json)) {\n jsonOutput(env);\n }\n\n console.log(`Environment: ${env.id}`);\n console.log(` Name: ${env.name}`);\n console.log(` Description: ${env.description ?? 'N/A'}`);\n console.log(` Base URL: ${env.baseUrl ?? 'N/A'}`);\n console.log(` Created: ${env.createdAt}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestEnvironment } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface UpdateOptions {\n json?: boolean;\n name?: string;\n description?: string;\n baseUrl?: string;\n}\n\nexport async function runUpdate(id: string, options: UpdateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = {};\n if (options.name) body.name = options.name;\n if (options.description) body.description = options.description;\n if (options.baseUrl) body.baseUrl = options.baseUrl;\n\n const env = await client.patch<TestEnvironment>(`/testing/environments/${id}`, body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(env);\n }\n\n console.log(`Updated environment: ${env.id}`);\n console.log(` Name: ${env.name}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestRun } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface CreateOptions {\n json?: boolean;\n name: string;\n environment?: string;\n environmentId?: string;\n deploymentId?: string;\n tool?: string;\n runnerName?: string;\n runnerUrl?: string;\n}\n\nexport async function runCreate(options: CreateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = { name: options.name };\n if (options.environment) body.environment = options.environment;\n if (options.environmentId) body.environmentId = options.environmentId;\n if (options.deploymentId) body.deploymentId = options.deploymentId;\n if (options.tool) body.tool = options.tool;\n if (options.runnerName) body.runnerName = options.runnerName;\n if (options.runnerUrl) body.runnerUrl = options.runnerUrl;\n\n const run = await client.post<TestRun>('/testing/runs', body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(run);\n }\n\n console.log(`Created test run: ${run.id}`);\n console.log(` Name: ${run.name}`);\n console.log(` Status: ${run.status}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { PaginatedResponse, TestRun } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface ListOptions {\n json?: boolean;\n status?: string;\n environmentId?: string;\n tool?: string;\n limit?: string;\n offset?: string;\n}\n\nexport async function runList(options: ListOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const result = await client.get<PaginatedResponse<TestRun>>('/testing/runs', {\n status: options.status,\n environmentId: options.environmentId,\n tool: options.tool,\n limit: options.limit,\n offset: options.offset,\n });\n\n if (!isInteractive(options.json)) {\n jsonOutput(result);\n }\n\n console.log(`Test Runs (${result.data.length} of ${result.pagination.total}):\\n`);\n for (const run of result.data) {\n const summary = run.summary ? ` [${run.summary.passed ?? 0}P/${run.summary.failed ?? 0}F/${run.summary.skipped ?? 0}S]` : '';\n console.log(` ${run.id} ${run.status.padEnd(8)} ${run.name}${summary}`);\n }\n\n if (result.pagination.hasMore) {\n console.log(`\\n ... ${result.pagination.total - result.data.length} more. Use --offset to paginate.`);\n }\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestRun } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface GetOptions {\n json?: boolean;\n}\n\nexport async function runGet(id: string, options: GetOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const run = await client.get<TestRun>(`/testing/runs/${id}`);\n\n if (!isInteractive(options.json)) {\n jsonOutput(run);\n }\n\n console.log(`Test Run: ${run.id}`);\n console.log(` Name: ${run.name}`);\n console.log(` Status: ${run.status}`);\n console.log(` Tool: ${run.tool ?? 'N/A'}`);\n if (run.summary) {\n console.log(` Summary: ${run.summary.passed ?? 0} passed, ${run.summary.failed ?? 0} failed, ${run.summary.skipped ?? 0} skipped`);\n }\n if (run.durationMs) {\n console.log(` Duration: ${(run.durationMs / 1000).toFixed(1)}s`);\n }\n console.log(` Created: ${run.createdAt}`);\n if (run.completedAt) {\n console.log(` Completed: ${run.completedAt}`);\n }\n if (run.results && run.results.length > 0) {\n console.log(` Results: ${run.results.length} test(s)`);\n }\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestRun } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface UpdateOptions {\n json?: boolean;\n status?: string;\n}\n\nexport async function runUpdate(id: string, options: UpdateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = {};\n if (options.status) body.status = options.status;\n\n const run = await client.patch<TestRun>(`/testing/runs/${id}`, body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(run);\n }\n\n console.log(`Updated test run: ${run.id}`);\n console.log(` Status: ${run.status}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import { Box, Text } from 'ink';\nimport BigText from 'ink-big-text';\nimport Gradient from 'ink-gradient';\nimport React from 'react';\n\nexport function Banner({ title }: { title: string }) {\n return (\n <Box marginBottom={1} flexDirection=\"column\">\n <Gradient colors={['#f49f0a', '#ff6b6c']}>\n <BigText text=\"Smoo AI\" font=\"tiny\" />\n </Gradient>\n <Text bold>{title}</Text>\n </Box>\n );\n}\n","import { Box, Text } from 'ink';\nimport Spinner from 'ink-spinner';\nimport React from 'react';\n\nexport interface TaskItem {\n label: string;\n status: 'pending' | 'running' | 'done' | 'error';\n error?: string;\n}\n\nexport function TaskList({ tasks }: { tasks: TaskItem[] }) {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n {tasks.map((task, i) => (\n <Box key={i}>\n <Box width={3}>\n {task.status === 'running' && (\n <Text color=\"yellow\">\n <Spinner type=\"dots\" />\n </Text>\n )}\n {task.status === 'done' && <Text color=\"green\">✓</Text>}\n {task.status === 'error' && <Text color=\"red\">✗</Text>}\n {task.status === 'pending' && <Text color=\"gray\">○</Text>}\n </Box>\n <Text color={task.status === 'error' ? 'red' : task.status === 'done' ? 'green' : undefined}>{task.label}</Text>\n {task.error && <Text color=\"red\"> — {task.error}</Text>}\n </Box>\n ))}\n </Box>\n );\n}\n","/**\n * CTRF (Common Test Report Format) file parsing and validation.\n */\n\nimport { readFileSync } from 'fs';\nimport { z } from 'zod';\nimport type { CtrfReport } from '../../lib/types';\n\nconst CtrfTestSchema = z.object({\n name: z.string(),\n status: z.enum(['passed', 'failed', 'skipped', 'pending', 'other']),\n duration: z.number().optional(),\n suite: z.string().optional(),\n filePath: z.string().optional(),\n message: z.string().optional(),\n trace: z.string().optional(),\n retries: z.number().optional(),\n flaky: z.boolean().optional(),\n browser: z.string().optional(),\n tags: z.array(z.string()).optional(),\n extra: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst CtrfReportSchema = z.object({\n results: z.object({\n tool: z\n .object({\n name: z.string().optional(),\n version: z.string().optional(),\n })\n .optional(),\n summary: z\n .object({\n tests: z.number().optional(),\n passed: z.number().optional(),\n failed: z.number().optional(),\n skipped: z.number().optional(),\n pending: z.number().optional(),\n other: z.number().optional(),\n start: z.number().optional(),\n stop: z.number().optional(),\n })\n .optional(),\n tests: z.array(CtrfTestSchema).optional(),\n environment: z.record(z.string(), z.unknown()).optional(),\n }),\n});\n\nexport function parseCtrfFile(filePath: string): CtrfReport {\n let raw: string;\n try {\n raw = readFileSync(filePath, 'utf-8');\n } catch (err) {\n throw new Error(`Failed to read CTRF file: ${filePath} — ${err instanceof Error ? err.message : String(err)}`);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(`Invalid JSON in CTRF file: ${filePath}`);\n }\n\n const result = CtrfReportSchema.safeParse(parsed);\n if (!result.success) {\n const issues = result.error.issues.map((i) => ` ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid CTRF format:\\n${issues}`);\n }\n\n return result.data as CtrfReport;\n}\n\nexport function summarizeCtrfResults(report: CtrfReport): {\n total: number;\n passed: number;\n failed: number;\n skipped: number;\n pending: number;\n other: number;\n hasFailed: boolean;\n} {\n const summary = report.results.summary;\n if (summary) {\n const total = summary.tests ?? 0;\n const passed = summary.passed ?? 0;\n const failed = summary.failed ?? 0;\n const skipped = summary.skipped ?? 0;\n const pending = summary.pending ?? 0;\n const other = summary.other ?? 0;\n return { total, passed, failed, skipped, pending, other, hasFailed: failed > 0 };\n }\n\n // Derive from tests array if no summary\n const tests = report.results.tests ?? [];\n const counts = { total: tests.length, passed: 0, failed: 0, skipped: 0, pending: 0, other: 0 };\n for (const test of tests) {\n if (test.status in counts) {\n counts[test.status as keyof Omit<typeof counts, 'total'>]++;\n }\n }\n return { ...counts, hasFailed: counts.failed > 0 };\n}\n","import { basename } from 'path';\nimport { render, Box, Text } from 'ink';\nimport React, { useEffect, useState } from 'react';\nimport type { TestRun } from '../../../lib/types';\nimport { Banner } from '../../components/Banner';\nimport { TaskList, type TaskItem } from '../../components/TaskList';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { parseCtrfFile, summarizeCtrfResults } from '../../utils/ctrf';\nimport { isInteractive, jsonOutput, errorOutput } from '../../utils/output';\n\ninterface ReportOptions {\n json?: boolean;\n name?: string;\n environment?: string;\n deploymentId?: string;\n tool?: string;\n buildName?: string;\n buildUrl?: string;\n}\n\nexport async function reportLogic(\n ctrfFile: string,\n options: ReportOptions,\n): Promise<{\n run: TestRun;\n resultCount: number;\n summary: ReturnType<typeof summarizeCtrfResults>;\n}> {\n // 1. Parse CTRF file\n const report = parseCtrfFile(ctrfFile);\n const summary = summarizeCtrfResults(report);\n\n // 2. Authenticate\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n // 3. Create test run\n const runName = options.name ?? basename(ctrfFile, '.json');\n const runBody: Record<string, unknown> = {\n name: runName,\n tool: options.tool ?? report.results.tool?.name,\n buildName: options.buildName ?? process.env.GITHUB_SHA,\n };\n\n if (options.environment) runBody.environment = options.environment;\n if (options.deploymentId) runBody.deploymentId = options.deploymentId;\n\n // Build URL from GitHub Actions context\n if (options.buildUrl) {\n runBody.buildUrl = options.buildUrl;\n } else if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) {\n runBody.buildUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;\n }\n\n const run = await client.post<TestRun>('/testing/runs', runBody);\n\n // 4. Submit CTRF results\n let resultCount = 0;\n try {\n const resultResponse = await client.post<{ count: number }>(`/testing/runs/${run.id}/results`, {\n results: report.results,\n });\n resultCount = resultResponse.count;\n } catch (err) {\n // Mark run as errored if result submission fails\n await client.patch(`/testing/runs/${run.id}`, {\n status: 'errored',\n completedAt: new Date().toISOString(),\n });\n throw err;\n }\n\n // 5. Run status is automatically updated by the results endpoint\n // Fetch the updated run\n const updatedRun = await client.get<TestRun>(`/testing/runs/${run.id}`);\n\n return { run: updatedRun, resultCount, summary };\n}\n\nfunction ReportUI({ ctrfFile, options }: { ctrfFile: string; options: ReportOptions }) {\n const [tasks, setTasks] = useState<TaskItem[]>([\n { label: 'Parsing CTRF report', status: 'pending' },\n { label: 'Authenticating', status: 'pending' },\n { label: 'Creating test run', status: 'pending' },\n { label: 'Submitting results', status: 'pending' },\n ]);\n const [result, setResult] = useState<Awaited<ReturnType<typeof reportLogic>> | null>(null);\n\n useEffect(() => {\n (async () => {\n try {\n // Parse\n setTasks((t) => t.map((task, i) => (i === 0 ? { ...task, status: 'running' } : task)));\n const report = parseCtrfFile(ctrfFile);\n const summary = summarizeCtrfResults(report);\n setTasks((t) => t.map((task, i) => (i === 0 ? { ...task, status: 'done' } : task)));\n\n // Auth\n setTasks((t) => t.map((task, i) => (i === 1 ? { ...task, status: 'running' } : task)));\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n setTasks((t) => t.map((task, i) => (i === 1 ? { ...task, status: 'done' } : task)));\n\n // Create run\n setTasks((t) => t.map((task, i) => (i === 2 ? { ...task, status: 'running' } : task)));\n const runName = options.name ?? basename(ctrfFile, '.json');\n const runBody: Record<string, unknown> = {\n name: runName,\n tool: options.tool ?? report.results.tool?.name,\n buildName: options.buildName ?? process.env.GITHUB_SHA,\n };\n if (options.environment) runBody.environment = options.environment;\n if (options.deploymentId) runBody.deploymentId = options.deploymentId;\n if (options.buildUrl) {\n runBody.buildUrl = options.buildUrl;\n } else if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) {\n runBody.buildUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;\n }\n const run = await client.post<TestRun>('/testing/runs', runBody);\n setTasks((t) => t.map((task, i) => (i === 2 ? { ...task, status: 'done' } : task)));\n\n // Submit results\n setTasks((t) => t.map((task, i) => (i === 3 ? { ...task, status: 'running' } : task)));\n const resultResponse = await client.post<{ count: number }>(`/testing/runs/${run.id}/results`, {\n results: report.results,\n });\n setTasks((t) => t.map((task, i) => (i === 3 ? { ...task, status: 'done' } : task)));\n\n const updatedRun = await client.get<TestRun>(`/testing/runs/${run.id}`);\n setResult({ run: updatedRun, resultCount: resultResponse.count, summary });\n } catch (err) {\n setTasks((t) =>\n t.map((task) => (task.status === 'running' ? { ...task, status: 'error', error: err instanceof Error ? err.message : String(err) } : task)),\n );\n }\n })();\n }, []);\n\n return (\n <Box flexDirection=\"column\">\n <Banner title=\"Report Test Results\" />\n <TaskList tasks={tasks} />\n {result && (\n <Box marginTop={1} flexDirection=\"column\">\n <Text color={result.summary.hasFailed ? 'red' : 'green'} bold>\n {result.summary.hasFailed ? '✗ FAILED' : '✓ PASSED'} — {result.resultCount} results submitted\n </Text>\n <Text>\n {result.summary.passed} passed, {result.summary.failed} failed, {result.summary.skipped} skipped\n </Text>\n <Text dimColor>Run ID: {result.run.id}</Text>\n </Box>\n )}\n </Box>\n );\n}\n\nexport function runReport(ctrfFile: string, options: ReportOptions): void {\n if (!isInteractive(options.json)) {\n reportLogic(ctrfFile, options).then(\n (result) =>\n jsonOutput({\n success: true,\n runId: result.run.id,\n status: result.run.status,\n resultCount: result.resultCount,\n summary: result.summary,\n }),\n (err) => {\n errorOutput(err instanceof Error ? err.message : String(err));\n },\n );\n return;\n }\n render(<ReportUI ctrfFile={ctrfFile} options={options} />);\n}\n","import { render, Box, Text } from 'ink';\nimport React, { useEffect, useState } from 'react';\nimport type { Credentials } from '../../../lib/types';\nimport { Banner } from '../../components/Banner';\nimport { TaskList, type TaskItem } from '../../components/TaskList';\nimport { ApiClient } from '../../utils/api-client';\nimport { saveCredentials } from '../../utils/credentials';\nimport { isInteractive, jsonOutput } from '../../utils/output';\n\ninterface LoginOptions {\n json?: boolean;\n clientId?: string;\n clientSecret?: string;\n orgId?: string;\n apiUrl?: string;\n authUrl?: string;\n}\n\nconst DEFAULT_API_URL = 'https://api.production.smoo.ai';\nconst DEFAULT_AUTH_URL = 'https://auth.production.smoo.ai/token';\n\nexport async function loginLogic(options: LoginOptions): Promise<{ success: boolean; orgId: string }> {\n const clientId = options.clientId;\n const clientSecret = options.clientSecret;\n const orgId = options.orgId;\n const apiUrl = options.apiUrl ?? DEFAULT_API_URL;\n const authUrl = options.authUrl ?? DEFAULT_AUTH_URL;\n\n if (!clientId) throw new Error('Client ID is required. Use --client-id flag.');\n if (!clientSecret) throw new Error('Client secret is required. Use --client-secret flag.');\n if (!orgId) throw new Error('Organization ID is required. Use --org-id flag.');\n\n const credentials: Credentials = { clientId, clientSecret, orgId, apiUrl, authUrl };\n\n // Validate by making a test API call\n const client = new ApiClient(credentials);\n await client.get<unknown[]>('/testing/environments');\n\n // Save credentials\n saveCredentials(credentials);\n\n return { success: true, orgId };\n}\n\nfunction LoginUI({ options }: { options: LoginOptions }) {\n const [tasks, setTasks] = useState<TaskItem[]>([\n { label: 'Validating credentials', status: 'pending' },\n { label: 'Saving to ~/.smooai/credentials.json', status: 'pending' },\n ]);\n const [result, setResult] = useState<{ orgId: string } | null>(null);\n\n useEffect(() => {\n (async () => {\n setTasks((t) => t.map((task, i) => (i === 0 ? { ...task, status: 'running' } : task)));\n\n try {\n const res = await loginLogic(options);\n\n setTasks([\n { label: 'Validating credentials', status: 'done' },\n { label: 'Saving to ~/.smooai/credentials.json', status: 'done' },\n ]);\n setResult({ orgId: res.orgId });\n } catch (err) {\n setTasks((t) =>\n t.map((task) => (task.status === 'running' ? { ...task, status: 'error', error: err instanceof Error ? err.message : String(err) } : task)),\n );\n }\n })();\n }, []);\n\n return (\n <Box flexDirection=\"column\">\n <Banner title=\"Login\" />\n <TaskList tasks={tasks} />\n {result && (\n <Box marginTop={1}>\n <Text color=\"green\" bold>\n Logged in successfully! Organization: {result.orgId}\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n\nexport function runLogin(options: LoginOptions): void {\n if (!isInteractive(options.json)) {\n loginLogic(options).then(\n (result) => jsonOutput(result),\n (err) => jsonOutput({ success: false, error: err.message }, 1),\n );\n return;\n }\n render(<LoginUI options={options} />);\n}\n","import { clearCredentials } from '../../utils/credentials';\nimport { isInteractive, jsonOutput } from '../../utils/output';\n\ninterface LogoutOptions {\n json?: boolean;\n}\n\nexport function runLogout(options: LogoutOptions): void {\n clearCredentials();\n\n if (!isInteractive(options.json)) {\n jsonOutput({ success: true, message: 'Logged out' });\n }\n\n console.log('Logged out. M2M credentials removed from ~/.smooai/credentials.json');\n}\n","import { loadCredentials } from '../../utils/credentials';\nimport { isInteractive, jsonOutput } from '../../utils/output';\n\ninterface StatusOptions {\n json?: boolean;\n}\n\nexport function runStatus(options: StatusOptions): void {\n const creds = loadCredentials();\n\n if (!isInteractive(options.json)) {\n jsonOutput({\n loggedIn: !!creds,\n orgId: creds?.orgId ?? null,\n apiUrl: creds?.apiUrl ?? null,\n authUrl: creds?.authUrl ?? null,\n });\n }\n\n if (!creds) {\n console.log('Not logged in. Run `smooai-testing login` to authenticate.');\n return;\n }\n\n console.log(`Logged in`);\n console.log(` Organization: ${creds.orgId}`);\n console.log(` API URL: ${creds.apiUrl}`);\n console.log(` Auth URL: ${creds.authUrl}`);\n console.log(` Client ID: ${creds.clientId.slice(0, 8)}...`);\n}\n","import { Command } from 'commander';\nimport { createCasesCommand } from './commands/cases/index';\nimport { createDeploymentsCommand } from './commands/deployments/index';\nimport { createEnvironmentsCommand } from './commands/environments/index';\nimport { createRunsCommand } from './commands/runs/index';\n\nconst program = new Command();\n\nprogram.name('smooai-testing').description('Smoo AI Testing SDK — manage test runs, cases, environments, and deployments').version('0.1.0');\n\n// Global --json flag\nprogram.option('--json', 'Output in JSON format (auto-enabled when no TTY detected)');\n\n// Auth commands\nprogram\n .command('login')\n .description('Store M2M credentials for CLI access')\n .requiredOption('--client-id <id>', 'M2M client ID')\n .requiredOption('--client-secret <secret>', 'M2M client secret')\n .requiredOption('--org-id <id>', 'Organization ID')\n .option('--api-url <url>', 'API base URL', 'https://api.production.smoo.ai')\n .option('--auth-url <url>', 'Auth token URL', 'https://auth.production.smoo.ai/token')\n .action(async (opts) => {\n const { runLogin } = await import('./commands/auth/login');\n runLogin({ ...opts, json: program.opts().json ?? opts.json });\n });\n\nprogram\n .command('logout')\n .description('Remove stored credentials')\n .action(async (opts) => {\n const { runLogout } = await import('./commands/auth/logout');\n runLogout({ json: program.opts().json ?? opts.json });\n });\n\nprogram\n .command('status')\n .description('Show current authentication status')\n .action(async (opts) => {\n const { runStatus } = await import('./commands/auth/status');\n runStatus({ json: program.opts().json ?? opts.json });\n });\n\n// Resource command groups\ncreateRunsCommand(program);\ncreateCasesCommand(program);\ncreateEnvironmentsCommand(program);\ncreateDeploymentsCommand(program);\n\nprogram.parse();\n","import { Command } from 'commander';\n\nexport function createCasesCommand(program: Command): Command {\n const cases = program.command('cases').description('Manage test cases');\n\n cases\n .command('create')\n .description('Create a test case')\n .requiredOption('--title <title>', 'Test case title')\n .option('--description <desc>', 'Description')\n .option('--priority <priority>', 'Priority (e.g., critical, high, medium, low)')\n .option('--automation-status <status>', 'Automation status')\n .option('--tags <tags>', 'Comma-separated tags')\n .action(async (opts) => {\n const { runCreate } = await import('./create');\n runCreate({ ...opts, json: program.opts().json ?? opts.json });\n });\n\n cases\n .command('list')\n .description('List test cases')\n .option('--tags <tags>', 'Filter by comma-separated tags')\n .option('--priority <priority>', 'Filter by priority')\n .option('--automation-status <status>', 'Filter by automation status')\n .option('--limit <n>', 'Max results', '50')\n .option('--offset <n>', 'Offset', '0')\n .action(async (opts) => {\n const { runList } = await import('./list');\n runList({ ...opts, json: program.opts().json ?? opts.json });\n });\n\n cases\n .command('get')\n .description('Get a test case by ID')\n .argument('<id>', 'Test case ID')\n .action(async (id, opts) => {\n const { runGet } = await import('./get');\n runGet(id, { json: program.opts().json ?? opts.json });\n });\n\n cases\n .command('update')\n .description('Update a test case')\n .argument('<id>', 'Test case ID')\n .option('--title <title>', 'New title')\n .option('--description <desc>', 'New description')\n .option('--priority <priority>', 'New priority')\n .option('--automation-status <status>', 'New automation status')\n .option('--tags <tags>', 'New comma-separated tags')\n .action(async (id, opts) => {\n const { runUpdate } = await import('./update');\n runUpdate(id, { ...opts, json: program.opts().json ?? opts.json });\n });\n\n cases\n .command('delete')\n .description('Delete a test case')\n .argument('<id>', 'Test case ID')\n .action(async (id, opts) => {\n const { runDelete } = await import('./delete');\n runDelete(id, { json: program.opts().json ?? opts.json });\n });\n\n return cases;\n}\n","import { Command } from 'commander';\n\nexport function createDeploymentsCommand(program: Command): Command {\n const deployments = program.command('deployments').description('Manage deployments');\n\n deployments\n .command('create')\n .description('Create a deployment')\n .requiredOption('--name <name>', 'Deployment name')\n .option('--environment-id <id>', 'Environment ID')\n .option('--status <status>', 'Status (pending, in_progress, success, failure, cancelled)')\n .option('--source <source>', 'Source (e.g., github, gitlab)')\n .option('--external-id <id>', 'External ID')\n .option('--external-url <url>', 'External URL')\n .option('--ref <ref>', 'Git ref')\n .action(async (opts) => {\n const { runCreate } = await import('./create');\n runCreate({ ...opts, json: program.opts().json ?? opts.json });\n });\n\n deployments\n .command('list')\n .description('List deployments')\n .option('--status <status>', 'Filter by status')\n .option('--environment-id <id>', 'Filter by environment ID')\n .option('--source <source>', 'Filter by source')\n .option('--limit <n>', 'Max results', '50')\n .option('--offset <n>', 'Offset', '0')\n .action(async (opts) => {\n const { runList } = await import('./list');\n runList({ ...opts, json: program.opts().json ?? opts.json });\n });\n\n deployments\n .command('get')\n .description('Get a deployment by ID')\n .argument('<id>', 'Deployment ID')\n .action(async (id, opts) => {\n const { runGet } = await import('./get');\n runGet(id, { json: program.opts().json ?? opts.json });\n });\n\n deployments\n .command('update')\n .description('Update a deployment')\n .argument('<id>', 'Deployment ID')\n .option('--name <name>', 'New name')\n .option('--status <status>', 'New status')\n .option('--external-url <url>', 'New external URL')\n .action(async (id, opts) => {\n const { runUpdate } = await import('./update');\n runUpdate(id, { ...opts, json: program.opts().json ?? opts.json });\n });\n\n deployments\n .command('delete')\n .description('Delete a deployment')\n .argument('<id>', 'Deployment ID')\n .action(async (id, opts) => {\n const { runDelete } = await import('./delete');\n runDelete(id, { json: program.opts().json ?? opts.json });\n });\n\n return deployments;\n}\n","import { Command } from 'commander';\n\nexport function createEnvironmentsCommand(program: Command): Command {\n const envs = program.command('envs').description('Manage test environments');\n\n envs.command('create')\n .description('Create a test environment')\n .requiredOption('--name <name>', 'Environment name')\n .option('--description <desc>', 'Description')\n .option('--base-url <url>', 'Base URL')\n .action(async (opts) => {\n const { runCreate } = await import('./create');\n runCreate({ ...opts, json: program.opts().json ?? opts.json });\n });\n\n envs.command('list')\n .description('List test environments')\n .action(async (opts) => {\n const { runList } = await import('./list');\n runList({ json: program.opts().json ?? opts.json });\n });\n\n envs.command('get')\n .description('Get a test environment by ID')\n .argument('<id>', 'Environment ID')\n .action(async (id, opts) => {\n const { runGet } = await import('./get');\n runGet(id, { json: program.opts().json ?? opts.json });\n });\n\n envs.command('update')\n .description('Update a test environment')\n .argument('<id>', 'Environment ID')\n .option('--name <name>', 'New name')\n .option('--description <desc>', 'New description')\n .option('--base-url <url>', 'New base URL')\n .action(async (id, opts) => {\n const { runUpdate } = await import('./update');\n runUpdate(id, { ...opts, json: program.opts().json ?? opts.json });\n });\n\n return envs;\n}\n","import { Command } from 'commander';\n\nexport function createRunsCommand(program: Command): Command {\n const runs = program.command('runs').description('Manage test runs');\n\n runs.command('create')\n .description('Create a test run')\n .requiredOption('--name <name>', 'Run name')\n .option('--environment <name>', 'Environment name (auto-creates if needed)')\n .option('--environment-id <id>', 'Environment ID')\n .option('--deployment-id <id>', 'Deployment ID')\n .option('--tool <name>', 'Tool name (e.g., vitest, playwright)')\n .option('--runner-name <name>', 'Runner name')\n .option('--runner-url <url>', 'Runner URL')\n .action(async (opts) => {\n const { runCreate } = await import('./create');\n runCreate({ ...opts, json: program.opts().json ?? opts.json });\n });\n\n runs.command('list')\n .description('List test runs')\n .option('--status <status>', 'Filter by status')\n .option('--environment-id <id>', 'Filter by environment ID')\n .option('--tool <name>', 'Filter by tool')\n .option('--limit <n>', 'Max results', '50')\n .option('--offset <n>', 'Offset', '0')\n .action(async (opts) => {\n const { runList } = await import('./list');\n runList({ ...opts, json: program.opts().json ?? opts.json });\n });\n\n runs.command('get')\n .description('Get a test run by ID')\n .argument('<id>', 'Test run ID')\n .action(async (id, opts) => {\n const { runGet } = await import('./get');\n runGet(id, { json: program.opts().json ?? opts.json });\n });\n\n runs.command('update')\n .description('Update a test run')\n .argument('<id>', 'Test run ID')\n .option('--status <status>', 'New status')\n .action(async (id, opts) => {\n const { runUpdate } = await import('./update');\n runUpdate(id, { ...opts, json: program.opts().json ?? opts.json });\n });\n\n runs.command('report')\n .description('Report test results from a CTRF file (create run + submit results)')\n .argument('<ctrf-file>', 'Path to CTRF JSON report file')\n .option('--name <name>', 'Run name (defaults to filename)')\n .option('--environment <name>', 'Environment name')\n .option('--deployment-id <id>', 'Deployment ID')\n .option('--tool <name>', 'Override tool name from CTRF')\n .option('--build-name <name>', 'Build name (e.g., git SHA)')\n .option('--build-url <url>', 'Build URL (e.g., CI run link)')\n .action(async (ctrfFile, opts) => {\n const { runReport } = await import('./report');\n runReport(ctrfFile, { ...opts, json: program.opts().json ?? opts.json });\n });\n\n return runs;\n}\n"],"mappings":"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","names":["create_exports","runCreate","init_create","list_exports","runList","init_list","get_exports","runGet","init_get","update_exports","runUpdate","init_update","delete_exports","runDelete","init_delete","create_exports","runCreate","init_create","list_exports","runList","init_list","get_exports","runGet","init_get","update_exports","runUpdate","init_update","create_exports","runCreate","init_create","list_exports","runList","init_list","get_exports","runGet","init_get","update_exports","runUpdate","init_update","Box","Text","jsx","jsxs","readFileSync","Box","Text","jsx","jsxs","render","Box","Text","useEffect","useState","jsx","jsxs","DEFAULT_API_URL","DEFAULT_AUTH_URL","program","runCreate","runList","runGet","runUpdate","runDelete","program","runCreate","runList","runGet","runUpdate","runDelete","program","runCreate","runList","runGet","runUpdate","program","runCreate","runList","runGet","runUpdate","runReport","runLogin","runLogout","runStatus"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/utils/auth.ts","../src/cli/utils/api-client.ts","../src/cli/utils/credentials.ts","../src/cli/utils/output.ts","../src/cli/commands/cases/create.ts","../src/cli/commands/cases/list.ts","../src/cli/commands/cases/get.ts","../src/cli/commands/cases/update.ts","../src/cli/commands/cases/delete.ts","../src/cli/commands/deployments/create.ts","../src/cli/commands/deployments/list.ts","../src/cli/commands/deployments/get.ts","../src/cli/commands/deployments/update.ts","../src/cli/commands/deployments/delete.ts","../src/cli/commands/environments/create.ts","../src/cli/commands/environments/list.ts","../src/cli/commands/environments/get.ts","../src/cli/commands/environments/update.ts","../src/cli/commands/runs/create.ts","../src/cli/commands/runs/list.ts","../src/cli/commands/runs/get.ts","../src/cli/commands/runs/update.ts","../src/cli/components/Banner.tsx","../src/cli/components/TaskList.tsx","../src/cli/utils/ctrf.ts","../src/cli/commands/runs/report.tsx","../src/cli/commands/auth/login.tsx","../src/cli/commands/auth/logout.ts","../src/cli/commands/auth/status.ts","../src/cli/index.ts","../src/cli/commands/cases/index.ts","../src/cli/commands/deployments/index.ts","../src/cli/commands/environments/index.ts","../src/cli/commands/runs/index.ts"],"sourcesContent":["/**\n * M2M token exchange for CLI authentication.\n * Authenticates via client_credentials grant type against the Smoo AI auth service.\n */\n\nimport type { Credentials, TokenResponse } from '../../lib/types';\n\nlet cachedToken: string | null = null;\nlet tokenExpiresAt = 0;\n\nexport async function getAuthToken(credentials: Credentials): Promise<string> {\n // Return cached token if still valid (with 60s buffer)\n if (cachedToken && Date.now() < tokenExpiresAt - 60_000) {\n return cachedToken;\n }\n\n const response = await fetch(credentials.authUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'client_credentials',\n provider: 'client_credentials',\n client_id: credentials.clientId,\n client_secret: credentials.clientSecret,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n throw new Error(`Authentication failed: HTTP ${response.status}${body ? ` — ${body}` : ''}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Authentication failed: no access_token in response');\n }\n\n cachedToken = data.access_token;\n // Default to 1h TTL if not specified\n tokenExpiresAt = Date.now() + (data.expires_in || 3600) * 1000;\n\n return cachedToken;\n}\n\nexport function clearTokenCache(): void {\n cachedToken = null;\n tokenExpiresAt = 0;\n}\n","/**\n * HTTP client wrapper with M2M auth for the Smoo AI Testing API.\n */\n\nimport type { Credentials } from '../../lib/types';\nimport { clearTokenCache, getAuthToken } from './auth';\n\nexport class ApiClient {\n private credentials: Credentials;\n private baseUrl: string;\n\n constructor(credentials: Credentials) {\n this.credentials = credentials;\n this.baseUrl = credentials.apiUrl.replace(/\\/+$/, '');\n }\n\n private orgPath(path: string): string {\n return `${this.baseUrl}/organizations/${this.credentials.orgId}${path}`;\n }\n\n private async request<T>(method: string, url: string, body?: unknown, retry = true): Promise<T> {\n const token = await getAuthToken(this.credentials);\n\n const response = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: body != null ? JSON.stringify(body) : undefined,\n });\n\n // Retry once on 401 (token may have expired)\n if (response.status === 401 && retry) {\n clearTokenCache();\n return this.request<T>(method, url, body, false);\n }\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new Error(`API error: HTTP ${response.status} ${response.statusText}${errorBody ? ` — ${errorBody}` : ''}`);\n }\n\n const text = await response.text();\n if (!text) return undefined as T;\n return JSON.parse(text) as T;\n }\n\n async get<T>(path: string, params?: Record<string, string | number | undefined>): Promise<T> {\n let url = this.orgPath(path);\n if (params) {\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value != null) searchParams.set(key, String(value));\n }\n const qs = searchParams.toString();\n if (qs) url += `?${qs}`;\n }\n return this.request<T>('GET', url);\n }\n\n async post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('POST', this.orgPath(path), body);\n }\n\n async patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PATCH', this.orgPath(path), body);\n }\n\n async delete<T>(path: string): Promise<T> {\n return this.request<T>('DELETE', this.orgPath(path));\n }\n\n /**\n * Create a new ApiClient with the same auth credentials but a different org ID.\n * Useful for pushing results to multiple organizations with cross-org M2M tokens.\n */\n withOrgId(orgId: string): ApiClient {\n return new ApiClient({ ...this.credentials, orgId });\n }\n}\n","/**\n * Manage CLI credentials stored at ~/.smooai/credentials.json.\n * Shared with @smooai/config — same file, extended fields for M2M auth.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport type { Credentials } from '../../lib/types';\n\nconst SMOOAI_DIR = join(homedir(), '.smooai');\nconst CREDENTIALS_FILE = join(SMOOAI_DIR, 'credentials.json');\n\nconst DEFAULT_API_URL = 'https://api.production.smoo.ai';\nconst DEFAULT_AUTH_URL = 'https://auth.production.smoo.ai/token';\n\nexport function loadCredentials(): Credentials | null {\n // Try env vars first (CI environments)\n const envClientId = process.env.SMOOAI_CLIENT_ID;\n const envClientSecret = process.env.SMOOAI_CLIENT_SECRET;\n const envOrgId = process.env.SMOOAI_ORG_ID;\n\n if (envClientId && envClientSecret && envOrgId) {\n return {\n clientId: envClientId,\n clientSecret: envClientSecret,\n orgId: envOrgId,\n apiUrl: process.env.SMOOAI_API_URL || DEFAULT_API_URL,\n authUrl: process.env.SMOOAI_AUTH_URL || DEFAULT_AUTH_URL,\n };\n }\n\n // Fall back to credentials file\n try {\n if (!existsSync(CREDENTIALS_FILE)) return null;\n const raw = readFileSync(CREDENTIALS_FILE, 'utf-8');\n const parsed = JSON.parse(raw);\n if (!parsed.clientId || !parsed.clientSecret || !parsed.orgId) return null;\n return {\n clientId: parsed.clientId,\n clientSecret: parsed.clientSecret,\n orgId: parsed.orgId,\n apiUrl: parsed.apiUrl || DEFAULT_API_URL,\n authUrl: parsed.authUrl || DEFAULT_AUTH_URL,\n };\n } catch {\n return null;\n }\n}\n\nexport function saveCredentials(credentials: Credentials): void {\n mkdirSync(SMOOAI_DIR, { recursive: true });\n\n // Merge with existing credentials to preserve other SDK fields\n let existing: Record<string, unknown> = {};\n try {\n if (existsSync(CREDENTIALS_FILE)) {\n existing = JSON.parse(readFileSync(CREDENTIALS_FILE, 'utf-8'));\n }\n } catch {\n // ignore\n }\n\n const merged = { ...existing, ...credentials };\n writeFileSync(CREDENTIALS_FILE, JSON.stringify(merged, null, 2), { mode: 0o600 });\n}\n\nexport function clearCredentials(): void {\n if (!existsSync(CREDENTIALS_FILE)) return;\n\n try {\n const existing = JSON.parse(readFileSync(CREDENTIALS_FILE, 'utf-8'));\n // Remove only testing SDK fields, preserve @smooai/config fields\n delete existing.clientId;\n delete existing.clientSecret;\n // Keep orgId, apiUrl, authUrl as they may be shared\n writeFileSync(CREDENTIALS_FILE, JSON.stringify(existing, null, 2), { mode: 0o600 });\n } catch {\n // ignore\n }\n}\n\nexport function getCredentialsOrExit(): Credentials {\n const creds = loadCredentials();\n if (!creds) {\n console.error('Not logged in. Run `smooai-testing login` first, or set SMOOAI_CLIENT_ID, SMOOAI_CLIENT_SECRET, and SMOOAI_ORG_ID env vars.');\n process.exit(1);\n }\n return creds;\n}\n","/**\n * Dual-mode output utilities for CLI commands.\n * Supports both interactive (Ink TUI) and non-interactive (JSON) output.\n */\n\nexport function isInteractive(jsonFlag?: boolean): boolean {\n if (jsonFlag) return false;\n return Boolean(process.stdout.isTTY);\n}\n\nexport function jsonOutput(data: unknown, exitCode = 0): never {\n console.log(JSON.stringify(data, null, 2));\n process.exit(exitCode);\n}\n\nexport function errorOutput(message: string, details?: unknown): never {\n if (isInteractive()) {\n console.error(`Error: ${message}`);\n if (details) console.error(details);\n process.exit(1);\n }\n jsonOutput({ success: false, error: message, details }, 1);\n}\n","import type { TestCase } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface CreateOptions {\n json?: boolean;\n title: string;\n description?: string;\n priority?: string;\n automationStatus?: string;\n tags?: string;\n}\n\nexport async function runCreate(options: CreateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = { title: options.title };\n if (options.description) body.description = options.description;\n if (options.priority) body.priority = options.priority;\n if (options.automationStatus) body.automationStatus = options.automationStatus;\n if (options.tags) body.tags = options.tags.split(',').map((t) => t.trim());\n\n const tc = await client.post<TestCase>('/testing/cases', body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(tc);\n }\n\n console.log(`Created test case: ${tc.id}`);\n console.log(` Title: ${tc.title}`);\n console.log(` Priority: ${tc.priority ?? 'N/A'}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { PaginatedResponse, TestCase } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface ListOptions {\n json?: boolean;\n tags?: string;\n priority?: string;\n automationStatus?: string;\n limit?: string;\n offset?: string;\n}\n\nexport async function runList(options: ListOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const result = await client.get<PaginatedResponse<TestCase>>('/testing/cases', {\n tags: options.tags,\n priority: options.priority,\n automationStatus: options.automationStatus,\n limit: options.limit,\n offset: options.offset,\n });\n\n if (!isInteractive(options.json)) {\n jsonOutput(result);\n }\n\n console.log(`Test Cases (${result.data.length} of ${result.pagination.total}):\\n`);\n for (const tc of result.data) {\n const tags = tc.tags?.length ? ` [${tc.tags.join(', ')}]` : '';\n console.log(` ${tc.id} ${(tc.priority ?? '-').padEnd(8)} ${tc.title}${tags}`);\n }\n\n if (result.pagination.hasMore) {\n console.log(`\\n ... ${result.pagination.total - result.data.length} more. Use --offset to paginate.`);\n }\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestCase } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface GetOptions {\n json?: boolean;\n}\n\nexport async function runGet(id: string, options: GetOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const tc = await client.get<TestCase>(`/testing/cases/${id}`);\n\n if (!isInteractive(options.json)) {\n jsonOutput(tc);\n }\n\n console.log(`Test Case: ${tc.id}`);\n console.log(` Title: ${tc.title}`);\n console.log(` Priority: ${tc.priority ?? 'N/A'}`);\n console.log(` Automation: ${tc.automationStatus ?? 'N/A'}`);\n if (tc.description) console.log(` Description: ${tc.description}`);\n if (tc.tags?.length) console.log(` Tags: ${tc.tags.join(', ')}`);\n console.log(` Created: ${tc.createdAt}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestCase } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface UpdateOptions {\n json?: boolean;\n title?: string;\n description?: string;\n priority?: string;\n automationStatus?: string;\n tags?: string;\n}\n\nexport async function runUpdate(id: string, options: UpdateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = {};\n if (options.title) body.title = options.title;\n if (options.description) body.description = options.description;\n if (options.priority) body.priority = options.priority;\n if (options.automationStatus) body.automationStatus = options.automationStatus;\n if (options.tags) body.tags = options.tags.split(',').map((t) => t.trim());\n\n const tc = await client.patch<TestCase>(`/testing/cases/${id}`, body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(tc);\n }\n\n console.log(`Updated test case: ${tc.id}`);\n console.log(` Title: ${tc.title}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface DeleteOptions {\n json?: boolean;\n}\n\nexport async function runDelete(id: string, options: DeleteOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n await client.delete(`/testing/cases/${id}`);\n\n if (!isInteractive(options.json)) {\n jsonOutput({ success: true, id });\n }\n\n console.log(`Deleted test case: ${id}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { Deployment } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface CreateOptions {\n json?: boolean;\n name: string;\n environmentId?: string;\n status?: string;\n source?: string;\n externalId?: string;\n externalUrl?: string;\n ref?: string;\n}\n\nexport async function runCreate(options: CreateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = { name: options.name };\n if (options.environmentId) body.environmentId = options.environmentId;\n if (options.status) body.status = options.status;\n if (options.source) body.source = options.source;\n if (options.externalId) body.externalId = options.externalId;\n if (options.externalUrl) body.externalUrl = options.externalUrl;\n if (options.ref) body.ref = options.ref;\n\n const deployment = await client.post<Deployment>('/testing/deployments', body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(deployment);\n }\n\n console.log(`Created deployment: ${deployment.id}`);\n console.log(` Name: ${deployment.name}`);\n console.log(` Status: ${deployment.status}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { Deployment, PaginatedResponse } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface ListOptions {\n json?: boolean;\n status?: string;\n environmentId?: string;\n source?: string;\n limit?: string;\n offset?: string;\n}\n\nexport async function runList(options: ListOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const result = await client.get<PaginatedResponse<Deployment>>('/testing/deployments', {\n status: options.status,\n environmentId: options.environmentId,\n source: options.source,\n limit: options.limit,\n offset: options.offset,\n });\n\n if (!isInteractive(options.json)) {\n jsonOutput(result);\n }\n\n console.log(`Deployments (${result.data.length} of ${result.pagination.total}):\\n`);\n for (const d of result.data) {\n const ref = d.ref ? ` (${d.ref})` : '';\n console.log(` ${d.id} ${d.status.padEnd(12)} ${d.name}${ref}`);\n }\n\n if (result.pagination.hasMore) {\n console.log(`\\n ... ${result.pagination.total - result.data.length} more. Use --offset to paginate.`);\n }\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { Deployment } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface GetOptions {\n json?: boolean;\n}\n\nexport async function runGet(id: string, options: GetOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const deployment = await client.get<Deployment>(`/testing/deployments/${id}`);\n\n if (!isInteractive(options.json)) {\n jsonOutput(deployment);\n }\n\n console.log(`Deployment: ${deployment.id}`);\n console.log(` Name: ${deployment.name}`);\n console.log(` Status: ${deployment.status}`);\n console.log(` Source: ${deployment.source ?? 'N/A'}`);\n console.log(` Ref: ${deployment.ref ?? 'N/A'}`);\n console.log(` External URL: ${deployment.externalUrl ?? 'N/A'}`);\n console.log(` Created: ${deployment.createdAt}`);\n if (deployment.completedAt) {\n console.log(` Completed: ${deployment.completedAt}`);\n }\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { Deployment } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface UpdateOptions {\n json?: boolean;\n name?: string;\n status?: string;\n externalUrl?: string;\n}\n\nexport async function runUpdate(id: string, options: UpdateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = {};\n if (options.name) body.name = options.name;\n if (options.status) body.status = options.status;\n if (options.externalUrl) body.externalUrl = options.externalUrl;\n\n const deployment = await client.patch<Deployment>(`/testing/deployments/${id}`, body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(deployment);\n }\n\n console.log(`Updated deployment: ${deployment.id}`);\n console.log(` Status: ${deployment.status}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface DeleteOptions {\n json?: boolean;\n}\n\nexport async function runDelete(id: string, options: DeleteOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n await client.delete(`/testing/deployments/${id}`);\n\n if (!isInteractive(options.json)) {\n jsonOutput({ success: true, id });\n }\n\n console.log(`Deleted deployment: ${id}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestEnvironment } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface CreateOptions {\n json?: boolean;\n name: string;\n description?: string;\n baseUrl?: string;\n}\n\nexport async function runCreate(options: CreateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = { name: options.name };\n if (options.description) body.description = options.description;\n if (options.baseUrl) body.baseUrl = options.baseUrl;\n\n const env = await client.post<TestEnvironment>('/testing/environments', body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(env);\n }\n\n console.log(`Created environment: ${env.id}`);\n console.log(` Name: ${env.name}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestEnvironment } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface ListOptions {\n json?: boolean;\n}\n\nexport async function runList(options: ListOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const envs = await client.get<TestEnvironment[]>('/testing/environments');\n\n if (!isInteractive(options.json)) {\n jsonOutput(envs);\n }\n\n console.log(`Test Environments (${envs.length}):\\n`);\n for (const env of envs) {\n const url = env.baseUrl ? ` (${env.baseUrl})` : '';\n console.log(` ${env.id} ${env.name}${url}`);\n }\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestEnvironment } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface GetOptions {\n json?: boolean;\n}\n\nexport async function runGet(id: string, options: GetOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const env = await client.get<TestEnvironment>(`/testing/environments/${id}`);\n\n if (!isInteractive(options.json)) {\n jsonOutput(env);\n }\n\n console.log(`Environment: ${env.id}`);\n console.log(` Name: ${env.name}`);\n console.log(` Description: ${env.description ?? 'N/A'}`);\n console.log(` Base URL: ${env.baseUrl ?? 'N/A'}`);\n console.log(` Created: ${env.createdAt}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestEnvironment } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface UpdateOptions {\n json?: boolean;\n name?: string;\n description?: string;\n baseUrl?: string;\n}\n\nexport async function runUpdate(id: string, options: UpdateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = {};\n if (options.name) body.name = options.name;\n if (options.description) body.description = options.description;\n if (options.baseUrl) body.baseUrl = options.baseUrl;\n\n const env = await client.patch<TestEnvironment>(`/testing/environments/${id}`, body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(env);\n }\n\n console.log(`Updated environment: ${env.id}`);\n console.log(` Name: ${env.name}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestRun } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface CreateOptions {\n json?: boolean;\n name: string;\n environment?: string;\n environmentId?: string;\n deploymentId?: string;\n tool?: string;\n tags?: string;\n runnerName?: string;\n runnerUrl?: string;\n}\n\nexport async function runCreate(options: CreateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = { name: options.name };\n if (options.environment) body.environment = options.environment;\n if (options.environmentId) body.environmentId = options.environmentId;\n if (options.deploymentId) body.deploymentId = options.deploymentId;\n if (options.tool) body.tool = options.tool;\n if (options.tags) body.tags = options.tags.split(',').map((t: string) => t.trim());\n if (options.runnerName) body.runnerName = options.runnerName;\n if (options.runnerUrl) body.runnerUrl = options.runnerUrl;\n\n const run = await client.post<TestRun>('/testing/runs', body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(run);\n }\n\n console.log(`Created test run: ${run.id}`);\n console.log(` Name: ${run.name}`);\n console.log(` Status: ${run.status}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { PaginatedResponse, TestRun } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface ListOptions {\n json?: boolean;\n status?: string;\n environmentId?: string;\n tool?: string;\n tags?: string;\n limit?: string;\n offset?: string;\n}\n\nexport async function runList(options: ListOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const result = await client.get<PaginatedResponse<TestRun>>('/testing/runs', {\n status: options.status,\n environmentId: options.environmentId,\n tool: options.tool,\n tags: options.tags,\n limit: options.limit,\n offset: options.offset,\n });\n\n if (!isInteractive(options.json)) {\n jsonOutput(result);\n }\n\n console.log(`Test Runs (${result.data.length} of ${result.pagination.total}):\\n`);\n for (const run of result.data) {\n const summary = run.summary ? ` [${run.summary.passed ?? 0}P/${run.summary.failed ?? 0}F/${run.summary.skipped ?? 0}S]` : '';\n console.log(` ${run.id} ${run.status.padEnd(8)} ${run.name}${summary}`);\n }\n\n if (result.pagination.hasMore) {\n console.log(`\\n ... ${result.pagination.total - result.data.length} more. Use --offset to paginate.`);\n }\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestRun } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface GetOptions {\n json?: boolean;\n}\n\nexport async function runGet(id: string, options: GetOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const run = await client.get<TestRun>(`/testing/runs/${id}`);\n\n if (!isInteractive(options.json)) {\n jsonOutput(run);\n }\n\n console.log(`Test Run: ${run.id}`);\n console.log(` Name: ${run.name}`);\n console.log(` Status: ${run.status}`);\n console.log(` Tool: ${run.tool ?? 'N/A'}`);\n if (run.summary) {\n console.log(` Summary: ${run.summary.passed ?? 0} passed, ${run.summary.failed ?? 0} failed, ${run.summary.skipped ?? 0} skipped`);\n }\n if (run.durationMs) {\n console.log(` Duration: ${(run.durationMs / 1000).toFixed(1)}s`);\n }\n console.log(` Created: ${run.createdAt}`);\n if (run.completedAt) {\n console.log(` Completed: ${run.completedAt}`);\n }\n if (run.results && run.results.length > 0) {\n console.log(` Results: ${run.results.length} test(s)`);\n }\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import type { TestRun } from '../../../lib/types';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { errorOutput, isInteractive, jsonOutput } from '../../utils/output';\n\ninterface UpdateOptions {\n json?: boolean;\n status?: string;\n}\n\nexport async function runUpdate(id: string, options: UpdateOptions): Promise<void> {\n try {\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n const body: Record<string, unknown> = {};\n if (options.status) body.status = options.status;\n\n const run = await client.patch<TestRun>(`/testing/runs/${id}`, body);\n\n if (!isInteractive(options.json)) {\n jsonOutput(run);\n }\n\n console.log(`Updated test run: ${run.id}`);\n console.log(` Status: ${run.status}`);\n } catch (err) {\n errorOutput(err instanceof Error ? err.message : String(err));\n }\n}\n","import { Box, Text } from 'ink';\nimport BigText from 'ink-big-text';\nimport Gradient from 'ink-gradient';\nimport React from 'react';\n\nexport function Banner({ title }: { title: string }) {\n return (\n <Box marginBottom={1} flexDirection=\"column\">\n <Gradient colors={['#f49f0a', '#ff6b6c']}>\n <BigText text=\"Smoo AI\" font=\"tiny\" />\n </Gradient>\n <Text bold>{title}</Text>\n </Box>\n );\n}\n","import { Box, Text } from 'ink';\nimport Spinner from 'ink-spinner';\nimport React from 'react';\n\nexport interface TaskItem {\n label: string;\n status: 'pending' | 'running' | 'done' | 'error';\n error?: string;\n}\n\nexport function TaskList({ tasks }: { tasks: TaskItem[] }) {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n {tasks.map((task, i) => (\n <Box key={i}>\n <Box width={3}>\n {task.status === 'running' && (\n <Text color=\"yellow\">\n <Spinner type=\"dots\" />\n </Text>\n )}\n {task.status === 'done' && <Text color=\"green\">✓</Text>}\n {task.status === 'error' && <Text color=\"red\">✗</Text>}\n {task.status === 'pending' && <Text color=\"gray\">○</Text>}\n </Box>\n <Text color={task.status === 'error' ? 'red' : task.status === 'done' ? 'green' : undefined}>{task.label}</Text>\n {task.error && <Text color=\"red\"> — {task.error}</Text>}\n </Box>\n ))}\n </Box>\n );\n}\n","/**\n * CTRF (Common Test Report Format) file parsing and validation.\n */\n\nimport { readFileSync } from 'fs';\nimport { z } from 'zod';\nimport type { CtrfReport } from '../../lib/types';\n\nconst CtrfTestSchema = z.object({\n name: z.string(),\n status: z.enum(['passed', 'failed', 'skipped', 'pending', 'other']),\n duration: z.number().optional(),\n suite: z.string().optional(),\n filePath: z.string().optional(),\n message: z.string().optional(),\n trace: z.string().optional(),\n retries: z.number().optional(),\n flaky: z.boolean().optional(),\n browser: z.string().optional(),\n tags: z.array(z.string()).optional(),\n extra: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst CtrfReportSchema = z.object({\n results: z.object({\n tool: z\n .object({\n name: z.string().optional(),\n version: z.string().optional(),\n })\n .optional(),\n summary: z\n .object({\n tests: z.number().optional(),\n passed: z.number().optional(),\n failed: z.number().optional(),\n skipped: z.number().optional(),\n pending: z.number().optional(),\n other: z.number().optional(),\n start: z.number().optional(),\n stop: z.number().optional(),\n })\n .optional(),\n tests: z.array(CtrfTestSchema).optional(),\n environment: z.record(z.string(), z.unknown()).optional(),\n }),\n});\n\nexport function parseCtrfFile(filePath: string): CtrfReport {\n let raw: string;\n try {\n raw = readFileSync(filePath, 'utf-8');\n } catch (err) {\n throw new Error(`Failed to read CTRF file: ${filePath} — ${err instanceof Error ? err.message : String(err)}`);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(`Invalid JSON in CTRF file: ${filePath}`);\n }\n\n const result = CtrfReportSchema.safeParse(parsed);\n if (!result.success) {\n const issues = result.error.issues.map((i) => ` ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid CTRF format:\\n${issues}`);\n }\n\n return result.data as CtrfReport;\n}\n\nexport function summarizeCtrfResults(report: CtrfReport): {\n total: number;\n passed: number;\n failed: number;\n skipped: number;\n pending: number;\n other: number;\n hasFailed: boolean;\n} {\n const summary = report.results.summary;\n if (summary) {\n const total = summary.tests ?? 0;\n const passed = summary.passed ?? 0;\n const failed = summary.failed ?? 0;\n const skipped = summary.skipped ?? 0;\n const pending = summary.pending ?? 0;\n const other = summary.other ?? 0;\n return { total, passed, failed, skipped, pending, other, hasFailed: failed > 0 };\n }\n\n // Derive from tests array if no summary\n const tests = report.results.tests ?? [];\n const counts = { total: tests.length, passed: 0, failed: 0, skipped: 0, pending: 0, other: 0 };\n for (const test of tests) {\n if (test.status in counts) {\n counts[test.status as keyof Omit<typeof counts, 'total'>]++;\n }\n }\n return { ...counts, hasFailed: counts.failed > 0 };\n}\n","import { basename } from 'path';\nimport { render, Box, Text } from 'ink';\nimport React, { useEffect, useState } from 'react';\nimport type { TestRun, CtrfReport } from '../../../lib/types';\nimport { Banner } from '../../components/Banner';\nimport { TaskList, type TaskItem } from '../../components/TaskList';\nimport { ApiClient } from '../../utils/api-client';\nimport { getCredentialsOrExit } from '../../utils/credentials';\nimport { parseCtrfFile, summarizeCtrfResults } from '../../utils/ctrf';\nimport { isInteractive, jsonOutput, errorOutput } from '../../utils/output';\n\ninterface ReportOptions {\n json?: boolean;\n name?: string;\n environment?: string;\n deploymentId?: string;\n tool?: string;\n tags?: string;\n buildName?: string;\n buildUrl?: string;\n additionalOrgIds?: string;\n}\n\n/** Build the run body from options and parsed CTRF report */\nfunction buildRunBody(ctrfFile: string, report: CtrfReport, options: ReportOptions): Record<string, unknown> {\n const runName = options.name ?? basename(ctrfFile, '.json');\n const runBody: Record<string, unknown> = {\n name: runName,\n tool: options.tool ?? report.results.tool?.name,\n buildName: options.buildName ?? process.env.GITHUB_SHA,\n };\n\n if (options.environment) runBody.environment = options.environment;\n if (options.deploymentId) runBody.deploymentId = options.deploymentId;\n if (options.tags) runBody.tags = options.tags.split(',').map((t: string) => t.trim());\n\n if (options.buildUrl) {\n runBody.buildUrl = options.buildUrl;\n } else if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) {\n runBody.buildUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;\n }\n\n return runBody;\n}\n\n/** Create a test run and submit CTRF results using the given API client */\nasync function submitReportToOrg(client: ApiClient, report: CtrfReport, runBody: Record<string, unknown>): Promise<{ run: TestRun; resultCount: number }> {\n const run = await client.post<TestRun>('/testing/runs', runBody);\n\n let resultCount = 0;\n try {\n const resultResponse = await client.post<{ count: number }>(`/testing/runs/${run.id}/results`, {\n results: report.results,\n });\n resultCount = resultResponse.count;\n } catch (err) {\n // Mark run as errored if result submission fails\n await client.patch(`/testing/runs/${run.id}`, {\n status: 'errored',\n completedAt: new Date().toISOString(),\n });\n throw err;\n }\n\n const updatedRun = await client.get<TestRun>(`/testing/runs/${run.id}`);\n return { run: updatedRun, resultCount };\n}\n\n/** Parse additional org IDs from comma-separated string */\nfunction parseAdditionalOrgIds(raw?: string): string[] {\n if (!raw) return [];\n return raw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n}\n\nexport async function reportLogic(\n ctrfFile: string,\n options: ReportOptions,\n): Promise<{\n run: TestRun;\n resultCount: number;\n summary: ReturnType<typeof summarizeCtrfResults>;\n additionalResults: Array<{ orgId: string; run: TestRun; resultCount: number } | { orgId: string; error: string }>;\n}> {\n // 1. Parse CTRF file\n const report = parseCtrfFile(ctrfFile);\n const summary = summarizeCtrfResults(report);\n\n // 2. Authenticate\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n\n // 3. Build run body\n const runBody = buildRunBody(ctrfFile, report, options);\n\n // 4. Submit to primary org\n const { run, resultCount } = await submitReportToOrg(client, report, runBody);\n\n // 5. Submit to additional orgs\n const additionalOrgIds = parseAdditionalOrgIds(options.additionalOrgIds);\n const additionalResults: Array<{ orgId: string; run: TestRun; resultCount: number } | { orgId: string; error: string }> = [];\n\n for (const orgId of additionalOrgIds) {\n try {\n const altClient = client.withOrgId(orgId);\n const result = await submitReportToOrg(altClient, report, runBody);\n additionalResults.push({ orgId, run: result.run, resultCount: result.resultCount });\n } catch (err) {\n additionalResults.push({ orgId, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n return { run, resultCount, summary, additionalResults };\n}\n\nfunction ReportUI({ ctrfFile, options }: { ctrfFile: string; options: ReportOptions }) {\n const additionalOrgIds = parseAdditionalOrgIds(options.additionalOrgIds);\n const additionalOrgTasks: TaskItem[] = additionalOrgIds.map((orgId) => ({\n label: `Pushing results to org ${orgId}`,\n status: 'pending' as const,\n }));\n\n const [tasks, setTasks] = useState<TaskItem[]>([\n { label: 'Parsing CTRF report', status: 'pending' },\n { label: 'Authenticating', status: 'pending' },\n { label: 'Creating test run', status: 'pending' },\n { label: 'Submitting results', status: 'pending' },\n ...additionalOrgTasks,\n ]);\n const [result, setResult] = useState<Awaited<ReturnType<typeof reportLogic>> | null>(null);\n\n // Index where additional org tasks start\n const additionalStartIndex = 4;\n\n useEffect(() => {\n (async () => {\n try {\n // Parse\n setTasks((t) => t.map((task, i) => (i === 0 ? { ...task, status: 'running' } : task)));\n const report = parseCtrfFile(ctrfFile);\n const summary = summarizeCtrfResults(report);\n setTasks((t) => t.map((task, i) => (i === 0 ? { ...task, status: 'done' } : task)));\n\n // Auth\n setTasks((t) => t.map((task, i) => (i === 1 ? { ...task, status: 'running' } : task)));\n const creds = getCredentialsOrExit();\n const client = new ApiClient(creds);\n setTasks((t) => t.map((task, i) => (i === 1 ? { ...task, status: 'done' } : task)));\n\n // Build run body\n const runBody = buildRunBody(ctrfFile, report, options);\n\n // Create run\n setTasks((t) => t.map((task, i) => (i === 2 ? { ...task, status: 'running' } : task)));\n const run = await client.post<TestRun>('/testing/runs', runBody);\n setTasks((t) => t.map((task, i) => (i === 2 ? { ...task, status: 'done' } : task)));\n\n // Submit results\n setTasks((t) => t.map((task, i) => (i === 3 ? { ...task, status: 'running' } : task)));\n const resultResponse = await client.post<{ count: number }>(`/testing/runs/${run.id}/results`, {\n results: report.results,\n });\n setTasks((t) => t.map((task, i) => (i === 3 ? { ...task, status: 'done' } : task)));\n\n const updatedRun = await client.get<TestRun>(`/testing/runs/${run.id}`);\n\n // Submit to additional orgs\n const additionalResults: Array<{ orgId: string; run: TestRun; resultCount: number } | { orgId: string; error: string }> = [];\n\n for (let idx = 0; idx < additionalOrgIds.length; idx++) {\n const orgId = additionalOrgIds[idx];\n const taskIdx = additionalStartIndex + idx;\n setTasks((t) => t.map((task, i) => (i === taskIdx ? { ...task, status: 'running' } : task)));\n\n try {\n const altClient = client.withOrgId(orgId);\n const altResult = await submitReportToOrg(altClient, report, runBody);\n additionalResults.push({ orgId, run: altResult.run, resultCount: altResult.resultCount });\n setTasks((t) => t.map((task, i) => (i === taskIdx ? { ...task, status: 'done' } : task)));\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n additionalResults.push({ orgId, error: errorMsg });\n setTasks((t) => t.map((task, i) => (i === taskIdx ? { ...task, status: 'error', error: errorMsg } : task)));\n }\n }\n\n setResult({ run: updatedRun, resultCount: resultResponse.count, summary, additionalResults });\n } catch (err) {\n setTasks((t) =>\n t.map((task) => (task.status === 'running' ? { ...task, status: 'error', error: err instanceof Error ? err.message : String(err) } : task)),\n );\n }\n })();\n }, []);\n\n return (\n <Box flexDirection=\"column\">\n <Banner title=\"Report Test Results\" />\n <TaskList tasks={tasks} />\n {result && (\n <Box marginTop={1} flexDirection=\"column\">\n <Text color={result.summary.hasFailed ? 'red' : 'green'} bold>\n {result.summary.hasFailed ? '✗ FAILED' : '✓ PASSED'} — {result.resultCount} results submitted\n </Text>\n <Text>\n {result.summary.passed} passed, {result.summary.failed} failed, {result.summary.skipped} skipped\n </Text>\n <Text dimColor>Run ID: {result.run.id}</Text>\n {result.additionalResults.length > 0 && (\n <Box marginTop={1} flexDirection=\"column\">\n <Text bold>Additional orgs:</Text>\n {result.additionalResults.map((ar) =>\n 'error' in ar ? (\n <Text key={ar.orgId} color=\"red\">\n {' '}\n ✗ {ar.orgId}: {ar.error}\n </Text>\n ) : (\n <Text key={ar.orgId} color=\"green\">\n {' '}\n ✓ {ar.orgId}: Run {ar.run.id} — {ar.resultCount} results\n </Text>\n ),\n )}\n </Box>\n )}\n </Box>\n )}\n </Box>\n );\n}\n\nexport function runReport(ctrfFile: string, options: ReportOptions): void {\n if (!isInteractive(options.json)) {\n reportLogic(ctrfFile, options).then(\n (result) =>\n jsonOutput({\n success: true,\n runId: result.run.id,\n status: result.run.status,\n resultCount: result.resultCount,\n summary: result.summary,\n additionalResults: result.additionalResults.map((ar) =>\n 'error' in ar\n ? { orgId: ar.orgId, success: false, error: ar.error }\n : { orgId: ar.orgId, success: true, runId: ar.run.id, resultCount: ar.resultCount },\n ),\n }),\n (err) => {\n errorOutput(err instanceof Error ? err.message : String(err));\n },\n );\n return;\n }\n render(<ReportUI ctrfFile={ctrfFile} options={options} />);\n}\n","import { render, Box, Text } from 'ink';\nimport React, { useEffect, useState } from 'react';\nimport type { Credentials } from '../../../lib/types';\nimport { Banner } from '../../components/Banner';\nimport { TaskList, type TaskItem } from '../../components/TaskList';\nimport { ApiClient } from '../../utils/api-client';\nimport { saveCredentials } from '../../utils/credentials';\nimport { isInteractive, jsonOutput } from '../../utils/output';\n\ninterface LoginOptions {\n json?: boolean;\n clientId?: string;\n clientSecret?: string;\n orgId?: string;\n apiUrl?: string;\n authUrl?: string;\n}\n\nconst DEFAULT_API_URL = 'https://api.production.smoo.ai';\nconst DEFAULT_AUTH_URL = 'https://auth.production.smoo.ai/token';\n\nexport async function loginLogic(options: LoginOptions): Promise<{ success: boolean; orgId: string }> {\n const clientId = options.clientId;\n const clientSecret = options.clientSecret;\n const orgId = options.orgId;\n const apiUrl = options.apiUrl ?? DEFAULT_API_URL;\n const authUrl = options.authUrl ?? DEFAULT_AUTH_URL;\n\n if (!clientId) throw new Error('Client ID is required. Use --client-id flag.');\n if (!clientSecret) throw new Error('Client secret is required. Use --client-secret flag.');\n if (!orgId) throw new Error('Organization ID is required. Use --org-id flag.');\n\n const credentials: Credentials = { clientId, clientSecret, orgId, apiUrl, authUrl };\n\n // Validate by making a test API call\n const client = new ApiClient(credentials);\n await client.get<unknown[]>('/testing/environments');\n\n // Save credentials\n saveCredentials(credentials);\n\n return { success: true, orgId };\n}\n\nfunction LoginUI({ options }: { options: LoginOptions }) {\n const [tasks, setTasks] = useState<TaskItem[]>([\n { label: 'Validating credentials', status: 'pending' },\n { label: 'Saving to ~/.smooai/credentials.json', status: 'pending' },\n ]);\n const [result, setResult] = useState<{ orgId: string } | null>(null);\n\n useEffect(() => {\n (async () => {\n setTasks((t) => t.map((task, i) => (i === 0 ? { ...task, status: 'running' } : task)));\n\n try {\n const res = await loginLogic(options);\n\n setTasks([\n { label: 'Validating credentials', status: 'done' },\n { label: 'Saving to ~/.smooai/credentials.json', status: 'done' },\n ]);\n setResult({ orgId: res.orgId });\n } catch (err) {\n setTasks((t) =>\n t.map((task) => (task.status === 'running' ? { ...task, status: 'error', error: err instanceof Error ? err.message : String(err) } : task)),\n );\n }\n })();\n }, []);\n\n return (\n <Box flexDirection=\"column\">\n <Banner title=\"Login\" />\n <TaskList tasks={tasks} />\n {result && (\n <Box marginTop={1}>\n <Text color=\"green\" bold>\n Logged in successfully! Organization: {result.orgId}\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n\nexport function runLogin(options: LoginOptions): void {\n if (!isInteractive(options.json)) {\n loginLogic(options).then(\n (result) => jsonOutput(result),\n (err) => jsonOutput({ success: false, error: err.message }, 1),\n );\n return;\n }\n render(<LoginUI options={options} />);\n}\n","import { clearCredentials } from '../../utils/credentials';\nimport { isInteractive, jsonOutput } from '../../utils/output';\n\ninterface LogoutOptions {\n json?: boolean;\n}\n\nexport function runLogout(options: LogoutOptions): void {\n clearCredentials();\n\n if (!isInteractive(options.json)) {\n jsonOutput({ success: true, message: 'Logged out' });\n }\n\n console.log('Logged out. M2M credentials removed from ~/.smooai/credentials.json');\n}\n","import { loadCredentials } from '../../utils/credentials';\nimport { isInteractive, jsonOutput } from '../../utils/output';\n\ninterface StatusOptions {\n json?: boolean;\n}\n\nexport function runStatus(options: StatusOptions): void {\n const creds = loadCredentials();\n\n if (!isInteractive(options.json)) {\n jsonOutput({\n loggedIn: !!creds,\n orgId: creds?.orgId ?? null,\n apiUrl: creds?.apiUrl ?? null,\n authUrl: creds?.authUrl ?? null,\n });\n }\n\n if (!creds) {\n console.log('Not logged in. Run `smooai-testing login` to authenticate.');\n return;\n }\n\n console.log(`Logged in`);\n console.log(` Organization: ${creds.orgId}`);\n console.log(` API URL: ${creds.apiUrl}`);\n console.log(` Auth URL: ${creds.authUrl}`);\n console.log(` Client ID: ${creds.clientId.slice(0, 8)}...`);\n}\n","import { Command } from 'commander';\nimport { createCasesCommand } from './commands/cases/index';\nimport { createDeploymentsCommand } from './commands/deployments/index';\nimport { createEnvironmentsCommand } from './commands/environments/index';\nimport { createRunsCommand } from './commands/runs/index';\n\nconst program = new Command();\n\nprogram.name('smooai-testing').description('Smoo AI Testing SDK — manage test runs, cases, environments, and deployments').version('0.1.0');\n\n// Global --json flag\nprogram.option('--json', 'Output in JSON format (auto-enabled when no TTY detected)');\n\n// Auth commands\nprogram\n .command('login')\n .description('Store M2M credentials for CLI access')\n .requiredOption('--client-id <id>', 'M2M client ID')\n .requiredOption('--client-secret <secret>', 'M2M client secret')\n .requiredOption('--org-id <id>', 'Organization ID')\n .option('--api-url <url>', 'API base URL', 'https://api.production.smoo.ai')\n .option('--auth-url <url>', 'Auth token URL', 'https://auth.production.smoo.ai/token')\n .action(async (opts) => {\n const { runLogin } = await import('./commands/auth/login');\n runLogin({ ...opts, json: program.opts().json ?? opts.json });\n });\n\nprogram\n .command('logout')\n .description('Remove stored credentials')\n .action(async (opts) => {\n const { runLogout } = await import('./commands/auth/logout');\n runLogout({ json: program.opts().json ?? opts.json });\n });\n\nprogram\n .command('status')\n .description('Show current authentication status')\n .action(async (opts) => {\n const { runStatus } = await import('./commands/auth/status');\n runStatus({ json: program.opts().json ?? opts.json });\n });\n\n// Resource command groups\ncreateRunsCommand(program);\ncreateCasesCommand(program);\ncreateEnvironmentsCommand(program);\ncreateDeploymentsCommand(program);\n\nprogram.parse();\n","import { Command } from 'commander';\n\nexport function createCasesCommand(program: Command): Command {\n const cases = program.command('cases').description('Manage test cases');\n\n cases\n .command('create')\n .description('Create a test case')\n .requiredOption('--title <title>', 'Test case title')\n .option('--description <desc>', 'Description')\n .option('--priority <priority>', 'Priority (e.g., critical, high, medium, low)')\n .option('--automation-status <status>', 'Automation status')\n .option('--tags <tags>', 'Comma-separated tags')\n .action(async (opts) => {\n const { runCreate } = await import('./create');\n runCreate({ ...opts, json: program.opts().json ?? opts.json });\n });\n\n cases\n .command('list')\n .description('List test cases')\n .option('--tags <tags>', 'Filter by comma-separated tags')\n .option('--priority <priority>', 'Filter by priority')\n .option('--automation-status <status>', 'Filter by automation status')\n .option('--limit <n>', 'Max results', '50')\n .option('--offset <n>', 'Offset', '0')\n .action(async (opts) => {\n const { runList } = await import('./list');\n runList({ ...opts, json: program.opts().json ?? opts.json });\n });\n\n cases\n .command('get')\n .description('Get a test case by ID')\n .argument('<id>', 'Test case ID')\n .action(async (id, opts) => {\n const { runGet } = await import('./get');\n runGet(id, { json: program.opts().json ?? opts.json });\n });\n\n cases\n .command('update')\n .description('Update a test case')\n .argument('<id>', 'Test case ID')\n .option('--title <title>', 'New title')\n .option('--description <desc>', 'New description')\n .option('--priority <priority>', 'New priority')\n .option('--automation-status <status>', 'New automation status')\n .option('--tags <tags>', 'New comma-separated tags')\n .action(async (id, opts) => {\n const { runUpdate } = await import('./update');\n runUpdate(id, { ...opts, json: program.opts().json ?? opts.json });\n });\n\n cases\n .command('delete')\n .description('Delete a test case')\n .argument('<id>', 'Test case ID')\n .action(async (id, opts) => {\n const { runDelete } = await import('./delete');\n runDelete(id, { json: program.opts().json ?? opts.json });\n });\n\n return cases;\n}\n","import { Command } from 'commander';\n\nexport function createDeploymentsCommand(program: Command): Command {\n const deployments = program.command('deployments').description('Manage deployments');\n\n deployments\n .command('create')\n .description('Create a deployment')\n .requiredOption('--name <name>', 'Deployment name')\n .option('--environment-id <id>', 'Environment ID')\n .option('--status <status>', 'Status (pending, in_progress, success, failure, cancelled)')\n .option('--source <source>', 'Source (e.g., github, gitlab)')\n .option('--external-id <id>', 'External ID')\n .option('--external-url <url>', 'External URL')\n .option('--ref <ref>', 'Git ref')\n .action(async (opts) => {\n const { runCreate } = await import('./create');\n runCreate({ ...opts, json: program.opts().json ?? opts.json });\n });\n\n deployments\n .command('list')\n .description('List deployments')\n .option('--status <status>', 'Filter by status')\n .option('--environment-id <id>', 'Filter by environment ID')\n .option('--source <source>', 'Filter by source')\n .option('--limit <n>', 'Max results', '50')\n .option('--offset <n>', 'Offset', '0')\n .action(async (opts) => {\n const { runList } = await import('./list');\n runList({ ...opts, json: program.opts().json ?? opts.json });\n });\n\n deployments\n .command('get')\n .description('Get a deployment by ID')\n .argument('<id>', 'Deployment ID')\n .action(async (id, opts) => {\n const { runGet } = await import('./get');\n runGet(id, { json: program.opts().json ?? opts.json });\n });\n\n deployments\n .command('update')\n .description('Update a deployment')\n .argument('<id>', 'Deployment ID')\n .option('--name <name>', 'New name')\n .option('--status <status>', 'New status')\n .option('--external-url <url>', 'New external URL')\n .action(async (id, opts) => {\n const { runUpdate } = await import('./update');\n runUpdate(id, { ...opts, json: program.opts().json ?? opts.json });\n });\n\n deployments\n .command('delete')\n .description('Delete a deployment')\n .argument('<id>', 'Deployment ID')\n .action(async (id, opts) => {\n const { runDelete } = await import('./delete');\n runDelete(id, { json: program.opts().json ?? opts.json });\n });\n\n return deployments;\n}\n","import { Command } from 'commander';\n\nexport function createEnvironmentsCommand(program: Command): Command {\n const envs = program.command('envs').description('Manage test environments');\n\n envs.command('create')\n .description('Create a test environment')\n .requiredOption('--name <name>', 'Environment name')\n .option('--description <desc>', 'Description')\n .option('--base-url <url>', 'Base URL')\n .action(async (opts) => {\n const { runCreate } = await import('./create');\n runCreate({ ...opts, json: program.opts().json ?? opts.json });\n });\n\n envs.command('list')\n .description('List test environments')\n .action(async (opts) => {\n const { runList } = await import('./list');\n runList({ json: program.opts().json ?? opts.json });\n });\n\n envs.command('get')\n .description('Get a test environment by ID')\n .argument('<id>', 'Environment ID')\n .action(async (id, opts) => {\n const { runGet } = await import('./get');\n runGet(id, { json: program.opts().json ?? opts.json });\n });\n\n envs.command('update')\n .description('Update a test environment')\n .argument('<id>', 'Environment ID')\n .option('--name <name>', 'New name')\n .option('--description <desc>', 'New description')\n .option('--base-url <url>', 'New base URL')\n .action(async (id, opts) => {\n const { runUpdate } = await import('./update');\n runUpdate(id, { ...opts, json: program.opts().json ?? opts.json });\n });\n\n return envs;\n}\n","import { Command } from 'commander';\n\nexport function createRunsCommand(program: Command): Command {\n const runs = program.command('runs').description('Manage test runs');\n\n runs.command('create')\n .description('Create a test run')\n .requiredOption('--name <name>', 'Run name')\n .option('--environment <name>', 'Environment name (auto-creates if needed)')\n .option('--environment-id <id>', 'Environment ID')\n .option('--deployment-id <id>', 'Deployment ID')\n .option('--tool <name>', 'Tool name (e.g., vitest, playwright)')\n .option('--tags <tags>', 'Comma-separated tags (e.g., e2e,brent-rager)')\n .option('--runner-name <name>', 'Runner name')\n .option('--runner-url <url>', 'Runner URL')\n .action(async (opts) => {\n const { runCreate } = await import('./create');\n runCreate({ ...opts, json: program.opts().json ?? opts.json });\n });\n\n runs.command('list')\n .description('List test runs')\n .option('--status <status>', 'Filter by status')\n .option('--environment-id <id>', 'Filter by environment ID')\n .option('--tool <name>', 'Filter by tool')\n .option('--tags <tags>', 'Filter by tags (comma-separated)')\n .option('--limit <n>', 'Max results', '50')\n .option('--offset <n>', 'Offset', '0')\n .action(async (opts) => {\n const { runList } = await import('./list');\n runList({ ...opts, json: program.opts().json ?? opts.json });\n });\n\n runs.command('get')\n .description('Get a test run by ID')\n .argument('<id>', 'Test run ID')\n .action(async (id, opts) => {\n const { runGet } = await import('./get');\n runGet(id, { json: program.opts().json ?? opts.json });\n });\n\n runs.command('update')\n .description('Update a test run')\n .argument('<id>', 'Test run ID')\n .option('--status <status>', 'New status')\n .action(async (id, opts) => {\n const { runUpdate } = await import('./update');\n runUpdate(id, { ...opts, json: program.opts().json ?? opts.json });\n });\n\n runs.command('report')\n .description('Report test results from a CTRF file (create run + submit results)')\n .argument('<ctrf-file>', 'Path to CTRF JSON report file')\n .option('--name <name>', 'Run name (defaults to filename)')\n .option('--environment <name>', 'Environment name')\n .option('--deployment-id <id>', 'Deployment ID')\n .option('--tool <name>', 'Override tool name from CTRF')\n .option('--tags <tags>', 'Comma-separated tags (e.g., e2e,brent-rager)')\n .option('--build-name <name>', 'Build name (e.g., git SHA)')\n .option('--build-url <url>', 'Build URL (e.g., CI run link)')\n .option('--additional-org-ids <ids>', 'Comma-separated org IDs to also push results to')\n .action(async (ctrfFile, opts) => {\n const { runReport } = await import('./report');\n runReport(ctrfFile, { ...opts, json: program.opts().json ?? opts.json });\n });\n\n return runs;\n}\n"],"mappings":"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","names":["create_exports","runCreate","init_create","list_exports","runList","init_list","get_exports","runGet","init_get","update_exports","runUpdate","init_update","delete_exports","runDelete","init_delete","create_exports","runCreate","init_create","list_exports","runList","init_list","get_exports","runGet","init_get","update_exports","runUpdate","init_update","create_exports","runCreate","init_create","list_exports","runList","init_list","get_exports","runGet","init_get","update_exports","runUpdate","init_update","Box","Text","jsx","jsxs","readFileSync","Box","Text","jsx","jsxs","render","Box","Text","useEffect","useState","jsx","jsxs","DEFAULT_API_URL","DEFAULT_AUTH_URL","program","runCreate","runList","runGet","runUpdate","runDelete","program","runCreate","runList","runGet","runUpdate","runDelete","program","runCreate","runList","runGet","runUpdate","program","runCreate","runList","runGet","runUpdate","runReport","runLogin","runLogout","runStatus"]}
|
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -177,6 +177,7 @@ var SmooTestingClient = class {
|
|
|
177
177
|
environment: options?.environment,
|
|
178
178
|
deploymentId: options?.deploymentId,
|
|
179
179
|
tool: options?.tool ?? ctrf.results.tool?.name,
|
|
180
|
+
tags: options?.tags,
|
|
180
181
|
buildName: options?.buildName,
|
|
181
182
|
buildUrl: options?.buildUrl
|
|
182
183
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/lib/index.ts"],"sourcesContent":["export { SmooTestingClient } from './lib/index';\nexport type {\n Credentials,\n CtrfReport,\n CtrfTest,\n CreateDeploymentInput,\n CreateTestCaseInput,\n CreateTestEnvironmentInput,\n CreateTestRunInput,\n Deployment,\n DeploymentStatus,\n ListDeploymentsFilters,\n ListTestCasesFilters,\n ListTestRunsFilters,\n PaginatedResponse,\n SmooTestingClientOptions,\n TestCase,\n TestCaseStep,\n TestEnvironment,\n TestResult,\n TestRun,\n TestRunDeployment,\n TestRunSummary,\n TokenResponse,\n UpdateDeploymentInput,\n UpdateTestCaseInput,\n UpdateTestEnvironmentInput,\n UpdateTestRunInput,\n} from './lib/types';\n","/**\n * SmooTestingClient — programmatic library for the Smoo AI Testing API.\n */\n\nimport { readFileSync } from 'fs';\nimport type {\n CtrfReport,\n CreateDeploymentInput,\n CreateTestCaseInput,\n CreateTestEnvironmentInput,\n CreateTestRunInput,\n Credentials,\n Deployment,\n ListDeploymentsFilters,\n ListTestCasesFilters,\n ListTestRunsFilters,\n PaginatedResponse,\n SmooTestingClientOptions,\n TestCase,\n TestEnvironment,\n TestRun,\n TokenResponse,\n UpdateDeploymentInput,\n UpdateTestCaseInput,\n UpdateTestEnvironmentInput,\n UpdateTestRunInput,\n} from './types';\n\nconst DEFAULT_API_URL = 'https://api.production.smoo.ai';\nconst DEFAULT_AUTH_URL = 'https://auth.production.smoo.ai/token';\n\nexport class SmooTestingClient {\n private credentials: Credentials;\n private token: string | null = null;\n private tokenExpiresAt = 0;\n\n constructor(options: SmooTestingClientOptions) {\n this.credentials = {\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n orgId: options.orgId,\n apiUrl: (options.apiUrl ?? DEFAULT_API_URL).replace(/\\/+$/, ''),\n authUrl: options.authUrl ?? DEFAULT_AUTH_URL,\n };\n }\n\n // ── Auth ──\n\n private async authenticate(): Promise<string> {\n if (this.token && Date.now() < this.tokenExpiresAt - 60_000) {\n return this.token;\n }\n\n const response = await fetch(this.credentials.authUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'client_credentials',\n provider: 'client_credentials',\n client_id: this.credentials.clientId,\n client_secret: this.credentials.clientSecret,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n throw new Error(`Authentication failed: HTTP ${response.status}${body ? ` — ${body}` : ''}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n if (!data.access_token) {\n throw new Error('Authentication failed: no access_token in response');\n }\n\n this.token = data.access_token;\n this.tokenExpiresAt = Date.now() + (data.expires_in || 3600) * 1000;\n return this.token;\n }\n\n private async request<T>(method: string, path: string, body?: unknown, retry = true): Promise<T> {\n const token = await this.authenticate();\n const url = `${this.credentials.apiUrl}/organizations/${this.credentials.orgId}${path}`;\n\n const response = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: body != null ? JSON.stringify(body) : undefined,\n });\n\n if (response.status === 401 && retry) {\n this.token = null;\n this.tokenExpiresAt = 0;\n return this.request<T>(method, path, body, false);\n }\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new Error(`API error: HTTP ${response.status} ${response.statusText}${errorBody ? ` — ${errorBody}` : ''}`);\n }\n\n const text = await response.text();\n if (!text) return undefined as T;\n return JSON.parse(text) as T;\n }\n\n private buildQueryString(params?: Record<string, string | number | undefined>): string {\n if (!params) return '';\n const sp = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value != null) sp.set(key, String(value));\n }\n const qs = sp.toString();\n return qs ? `?${qs}` : '';\n }\n\n // ── Test Runs ──\n\n async createRun(input: CreateTestRunInput): Promise<TestRun> {\n return this.request<TestRun>('POST', '/testing/runs', input);\n }\n\n async listRuns(filters?: ListTestRunsFilters): Promise<PaginatedResponse<TestRun>> {\n const qs = this.buildQueryString(filters as Record<string, string | number | undefined>);\n return this.request<PaginatedResponse<TestRun>>('GET', `/testing/runs${qs}`);\n }\n\n async getRun(id: string): Promise<TestRun> {\n return this.request<TestRun>('GET', `/testing/runs/${id}`);\n }\n\n async updateRun(id: string, input: UpdateTestRunInput): Promise<TestRun> {\n return this.request<TestRun>('PATCH', `/testing/runs/${id}`, input);\n }\n\n async submitResults(runId: string, ctrf: CtrfReport): Promise<{ count: number }> {\n return this.request<{ count: number }>('POST', `/testing/runs/${runId}/results`, ctrf);\n }\n\n // ── Test Cases ──\n\n async createCase(input: CreateTestCaseInput): Promise<TestCase> {\n return this.request<TestCase>('POST', '/testing/cases', input);\n }\n\n async listCases(filters?: ListTestCasesFilters): Promise<PaginatedResponse<TestCase>> {\n const qs = this.buildQueryString(filters as Record<string, string | number | undefined>);\n return this.request<PaginatedResponse<TestCase>>('GET', `/testing/cases${qs}`);\n }\n\n async getCase(id: string): Promise<TestCase> {\n return this.request<TestCase>('GET', `/testing/cases/${id}`);\n }\n\n async updateCase(id: string, input: UpdateTestCaseInput): Promise<TestCase> {\n return this.request<TestCase>('PATCH', `/testing/cases/${id}`, input);\n }\n\n async deleteCase(id: string): Promise<void> {\n await this.request<unknown>('DELETE', `/testing/cases/${id}`);\n }\n\n // ── Test Environments ──\n\n async createEnvironment(input: CreateTestEnvironmentInput): Promise<TestEnvironment> {\n return this.request<TestEnvironment>('POST', '/testing/environments', input);\n }\n\n async listEnvironments(): Promise<TestEnvironment[]> {\n return this.request<TestEnvironment[]>('GET', '/testing/environments');\n }\n\n async getEnvironment(id: string): Promise<TestEnvironment> {\n return this.request<TestEnvironment>('GET', `/testing/environments/${id}`);\n }\n\n async updateEnvironment(id: string, input: UpdateTestEnvironmentInput): Promise<TestEnvironment> {\n return this.request<TestEnvironment>('PATCH', `/testing/environments/${id}`, input);\n }\n\n // ── Deployments ──\n\n async createDeployment(input: CreateDeploymentInput): Promise<Deployment> {\n return this.request<Deployment>('POST', '/testing/deployments', input);\n }\n\n async listDeployments(filters?: ListDeploymentsFilters): Promise<PaginatedResponse<Deployment>> {\n const qs = this.buildQueryString(filters as Record<string, string | number | undefined>);\n return this.request<PaginatedResponse<Deployment>>('GET', `/testing/deployments${qs}`);\n }\n\n async getDeployment(id: string): Promise<Deployment> {\n return this.request<Deployment>('GET', `/testing/deployments/${id}`);\n }\n\n async updateDeployment(id: string, input: UpdateDeploymentInput): Promise<Deployment> {\n return this.request<Deployment>('PATCH', `/testing/deployments/${id}`, input);\n }\n\n async deleteDeployment(id: string): Promise<void> {\n await this.request<unknown>('DELETE', `/testing/deployments/${id}`);\n }\n\n // ── High-Level: Report ──\n\n /**\n * High-level method that creates a test run, submits CTRF results, and returns the updated run.\n */\n async report(\n ctrfFilePath: string,\n options?: {\n name?: string;\n environment?: string;\n deploymentId?: string;\n tool?: string;\n buildName?: string;\n buildUrl?: string;\n },\n ): Promise<TestRun> {\n const raw = readFileSync(ctrfFilePath, 'utf-8');\n const ctrf = JSON.parse(raw) as CtrfReport;\n\n const run = await this.createRun({\n name: options?.name ?? ctrfFilePath,\n environment: options?.environment,\n deploymentId: options?.deploymentId,\n tool: options?.tool ?? ctrf.results.tool?.name,\n buildName: options?.buildName,\n buildUrl: options?.buildUrl,\n });\n\n try {\n await this.submitResults(run.id, ctrf);\n } catch (err) {\n await this.updateRun(run.id, {\n status: 'errored',\n completedAt: new Date().toISOString(),\n });\n throw err;\n }\n\n return this.getRun(run.id);\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/lib/index.ts"],"sourcesContent":["export { SmooTestingClient } from './lib/index';\nexport type {\n Credentials,\n CtrfReport,\n CtrfTest,\n CreateDeploymentInput,\n CreateTestCaseInput,\n CreateTestEnvironmentInput,\n CreateTestRunInput,\n Deployment,\n DeploymentStatus,\n ListDeploymentsFilters,\n ListTestCasesFilters,\n ListTestRunsFilters,\n PaginatedResponse,\n SmooTestingClientOptions,\n TestCase,\n TestCaseStep,\n TestEnvironment,\n TestResult,\n TestRun,\n TestRunDeployment,\n TestRunSummary,\n TokenResponse,\n UpdateDeploymentInput,\n UpdateTestCaseInput,\n UpdateTestEnvironmentInput,\n UpdateTestRunInput,\n} from './lib/types';\n","/**\n * SmooTestingClient — programmatic library for the Smoo AI Testing API.\n */\n\nimport { readFileSync } from 'fs';\nimport type {\n CtrfReport,\n CreateDeploymentInput,\n CreateTestCaseInput,\n CreateTestEnvironmentInput,\n CreateTestRunInput,\n Credentials,\n Deployment,\n ListDeploymentsFilters,\n ListTestCasesFilters,\n ListTestRunsFilters,\n PaginatedResponse,\n SmooTestingClientOptions,\n TestCase,\n TestEnvironment,\n TestRun,\n TokenResponse,\n UpdateDeploymentInput,\n UpdateTestCaseInput,\n UpdateTestEnvironmentInput,\n UpdateTestRunInput,\n} from './types';\n\nconst DEFAULT_API_URL = 'https://api.production.smoo.ai';\nconst DEFAULT_AUTH_URL = 'https://auth.production.smoo.ai/token';\n\nexport class SmooTestingClient {\n private credentials: Credentials;\n private token: string | null = null;\n private tokenExpiresAt = 0;\n\n constructor(options: SmooTestingClientOptions) {\n this.credentials = {\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n orgId: options.orgId,\n apiUrl: (options.apiUrl ?? DEFAULT_API_URL).replace(/\\/+$/, ''),\n authUrl: options.authUrl ?? DEFAULT_AUTH_URL,\n };\n }\n\n // ── Auth ──\n\n private async authenticate(): Promise<string> {\n if (this.token && Date.now() < this.tokenExpiresAt - 60_000) {\n return this.token;\n }\n\n const response = await fetch(this.credentials.authUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'client_credentials',\n provider: 'client_credentials',\n client_id: this.credentials.clientId,\n client_secret: this.credentials.clientSecret,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n throw new Error(`Authentication failed: HTTP ${response.status}${body ? ` — ${body}` : ''}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n if (!data.access_token) {\n throw new Error('Authentication failed: no access_token in response');\n }\n\n this.token = data.access_token;\n this.tokenExpiresAt = Date.now() + (data.expires_in || 3600) * 1000;\n return this.token;\n }\n\n private async request<T>(method: string, path: string, body?: unknown, retry = true): Promise<T> {\n const token = await this.authenticate();\n const url = `${this.credentials.apiUrl}/organizations/${this.credentials.orgId}${path}`;\n\n const response = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: body != null ? JSON.stringify(body) : undefined,\n });\n\n if (response.status === 401 && retry) {\n this.token = null;\n this.tokenExpiresAt = 0;\n return this.request<T>(method, path, body, false);\n }\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new Error(`API error: HTTP ${response.status} ${response.statusText}${errorBody ? ` — ${errorBody}` : ''}`);\n }\n\n const text = await response.text();\n if (!text) return undefined as T;\n return JSON.parse(text) as T;\n }\n\n private buildQueryString(params?: Record<string, string | number | undefined>): string {\n if (!params) return '';\n const sp = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value != null) sp.set(key, String(value));\n }\n const qs = sp.toString();\n return qs ? `?${qs}` : '';\n }\n\n // ── Test Runs ──\n\n async createRun(input: CreateTestRunInput): Promise<TestRun> {\n return this.request<TestRun>('POST', '/testing/runs', input);\n }\n\n async listRuns(filters?: ListTestRunsFilters): Promise<PaginatedResponse<TestRun>> {\n const qs = this.buildQueryString(filters as Record<string, string | number | undefined>);\n return this.request<PaginatedResponse<TestRun>>('GET', `/testing/runs${qs}`);\n }\n\n async getRun(id: string): Promise<TestRun> {\n return this.request<TestRun>('GET', `/testing/runs/${id}`);\n }\n\n async updateRun(id: string, input: UpdateTestRunInput): Promise<TestRun> {\n return this.request<TestRun>('PATCH', `/testing/runs/${id}`, input);\n }\n\n async submitResults(runId: string, ctrf: CtrfReport): Promise<{ count: number }> {\n return this.request<{ count: number }>('POST', `/testing/runs/${runId}/results`, ctrf);\n }\n\n // ── Test Cases ──\n\n async createCase(input: CreateTestCaseInput): Promise<TestCase> {\n return this.request<TestCase>('POST', '/testing/cases', input);\n }\n\n async listCases(filters?: ListTestCasesFilters): Promise<PaginatedResponse<TestCase>> {\n const qs = this.buildQueryString(filters as Record<string, string | number | undefined>);\n return this.request<PaginatedResponse<TestCase>>('GET', `/testing/cases${qs}`);\n }\n\n async getCase(id: string): Promise<TestCase> {\n return this.request<TestCase>('GET', `/testing/cases/${id}`);\n }\n\n async updateCase(id: string, input: UpdateTestCaseInput): Promise<TestCase> {\n return this.request<TestCase>('PATCH', `/testing/cases/${id}`, input);\n }\n\n async deleteCase(id: string): Promise<void> {\n await this.request<unknown>('DELETE', `/testing/cases/${id}`);\n }\n\n // ── Test Environments ──\n\n async createEnvironment(input: CreateTestEnvironmentInput): Promise<TestEnvironment> {\n return this.request<TestEnvironment>('POST', '/testing/environments', input);\n }\n\n async listEnvironments(): Promise<TestEnvironment[]> {\n return this.request<TestEnvironment[]>('GET', '/testing/environments');\n }\n\n async getEnvironment(id: string): Promise<TestEnvironment> {\n return this.request<TestEnvironment>('GET', `/testing/environments/${id}`);\n }\n\n async updateEnvironment(id: string, input: UpdateTestEnvironmentInput): Promise<TestEnvironment> {\n return this.request<TestEnvironment>('PATCH', `/testing/environments/${id}`, input);\n }\n\n // ── Deployments ──\n\n async createDeployment(input: CreateDeploymentInput): Promise<Deployment> {\n return this.request<Deployment>('POST', '/testing/deployments', input);\n }\n\n async listDeployments(filters?: ListDeploymentsFilters): Promise<PaginatedResponse<Deployment>> {\n const qs = this.buildQueryString(filters as Record<string, string | number | undefined>);\n return this.request<PaginatedResponse<Deployment>>('GET', `/testing/deployments${qs}`);\n }\n\n async getDeployment(id: string): Promise<Deployment> {\n return this.request<Deployment>('GET', `/testing/deployments/${id}`);\n }\n\n async updateDeployment(id: string, input: UpdateDeploymentInput): Promise<Deployment> {\n return this.request<Deployment>('PATCH', `/testing/deployments/${id}`, input);\n }\n\n async deleteDeployment(id: string): Promise<void> {\n await this.request<unknown>('DELETE', `/testing/deployments/${id}`);\n }\n\n // ── High-Level: Report ──\n\n /**\n * High-level method that creates a test run, submits CTRF results, and returns the updated run.\n */\n async report(\n ctrfFilePath: string,\n options?: {\n name?: string;\n environment?: string;\n deploymentId?: string;\n tool?: string;\n tags?: string[];\n buildName?: string;\n buildUrl?: string;\n },\n ): Promise<TestRun> {\n const raw = readFileSync(ctrfFilePath, 'utf-8');\n const ctrf = JSON.parse(raw) as CtrfReport;\n\n const run = await this.createRun({\n name: options?.name ?? ctrfFilePath,\n environment: options?.environment,\n deploymentId: options?.deploymentId,\n tool: options?.tool ?? ctrf.results.tool?.name,\n tags: options?.tags,\n buildName: options?.buildName,\n buildUrl: options?.buildUrl,\n });\n\n try {\n await this.submitResults(run.id, ctrf);\n } catch (err) {\n await this.updateRun(run.id, {\n status: 'errored',\n completedAt: new Date().toISOString(),\n });\n throw err;\n }\n\n return this.getRun(run.id);\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -151,6 +151,7 @@ var SmooTestingClient = class {
|
|
|
151
151
|
environment: options?.environment,
|
|
152
152
|
deploymentId: options?.deploymentId,
|
|
153
153
|
tool: options?.tool ?? ctrf.results.tool?.name,
|
|
154
|
+
tags: options?.tags,
|
|
154
155
|
buildName: options?.buildName,
|
|
155
156
|
buildUrl: options?.buildUrl
|
|
156
157
|
});
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/index.ts"],"sourcesContent":["/**\n * SmooTestingClient — programmatic library for the Smoo AI Testing API.\n */\n\nimport { readFileSync } from 'fs';\nimport type {\n CtrfReport,\n CreateDeploymentInput,\n CreateTestCaseInput,\n CreateTestEnvironmentInput,\n CreateTestRunInput,\n Credentials,\n Deployment,\n ListDeploymentsFilters,\n ListTestCasesFilters,\n ListTestRunsFilters,\n PaginatedResponse,\n SmooTestingClientOptions,\n TestCase,\n TestEnvironment,\n TestRun,\n TokenResponse,\n UpdateDeploymentInput,\n UpdateTestCaseInput,\n UpdateTestEnvironmentInput,\n UpdateTestRunInput,\n} from './types';\n\nconst DEFAULT_API_URL = 'https://api.production.smoo.ai';\nconst DEFAULT_AUTH_URL = 'https://auth.production.smoo.ai/token';\n\nexport class SmooTestingClient {\n private credentials: Credentials;\n private token: string | null = null;\n private tokenExpiresAt = 0;\n\n constructor(options: SmooTestingClientOptions) {\n this.credentials = {\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n orgId: options.orgId,\n apiUrl: (options.apiUrl ?? DEFAULT_API_URL).replace(/\\/+$/, ''),\n authUrl: options.authUrl ?? DEFAULT_AUTH_URL,\n };\n }\n\n // ── Auth ──\n\n private async authenticate(): Promise<string> {\n if (this.token && Date.now() < this.tokenExpiresAt - 60_000) {\n return this.token;\n }\n\n const response = await fetch(this.credentials.authUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'client_credentials',\n provider: 'client_credentials',\n client_id: this.credentials.clientId,\n client_secret: this.credentials.clientSecret,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n throw new Error(`Authentication failed: HTTP ${response.status}${body ? ` — ${body}` : ''}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n if (!data.access_token) {\n throw new Error('Authentication failed: no access_token in response');\n }\n\n this.token = data.access_token;\n this.tokenExpiresAt = Date.now() + (data.expires_in || 3600) * 1000;\n return this.token;\n }\n\n private async request<T>(method: string, path: string, body?: unknown, retry = true): Promise<T> {\n const token = await this.authenticate();\n const url = `${this.credentials.apiUrl}/organizations/${this.credentials.orgId}${path}`;\n\n const response = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: body != null ? JSON.stringify(body) : undefined,\n });\n\n if (response.status === 401 && retry) {\n this.token = null;\n this.tokenExpiresAt = 0;\n return this.request<T>(method, path, body, false);\n }\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new Error(`API error: HTTP ${response.status} ${response.statusText}${errorBody ? ` — ${errorBody}` : ''}`);\n }\n\n const text = await response.text();\n if (!text) return undefined as T;\n return JSON.parse(text) as T;\n }\n\n private buildQueryString(params?: Record<string, string | number | undefined>): string {\n if (!params) return '';\n const sp = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value != null) sp.set(key, String(value));\n }\n const qs = sp.toString();\n return qs ? `?${qs}` : '';\n }\n\n // ── Test Runs ──\n\n async createRun(input: CreateTestRunInput): Promise<TestRun> {\n return this.request<TestRun>('POST', '/testing/runs', input);\n }\n\n async listRuns(filters?: ListTestRunsFilters): Promise<PaginatedResponse<TestRun>> {\n const qs = this.buildQueryString(filters as Record<string, string | number | undefined>);\n return this.request<PaginatedResponse<TestRun>>('GET', `/testing/runs${qs}`);\n }\n\n async getRun(id: string): Promise<TestRun> {\n return this.request<TestRun>('GET', `/testing/runs/${id}`);\n }\n\n async updateRun(id: string, input: UpdateTestRunInput): Promise<TestRun> {\n return this.request<TestRun>('PATCH', `/testing/runs/${id}`, input);\n }\n\n async submitResults(runId: string, ctrf: CtrfReport): Promise<{ count: number }> {\n return this.request<{ count: number }>('POST', `/testing/runs/${runId}/results`, ctrf);\n }\n\n // ── Test Cases ──\n\n async createCase(input: CreateTestCaseInput): Promise<TestCase> {\n return this.request<TestCase>('POST', '/testing/cases', input);\n }\n\n async listCases(filters?: ListTestCasesFilters): Promise<PaginatedResponse<TestCase>> {\n const qs = this.buildQueryString(filters as Record<string, string | number | undefined>);\n return this.request<PaginatedResponse<TestCase>>('GET', `/testing/cases${qs}`);\n }\n\n async getCase(id: string): Promise<TestCase> {\n return this.request<TestCase>('GET', `/testing/cases/${id}`);\n }\n\n async updateCase(id: string, input: UpdateTestCaseInput): Promise<TestCase> {\n return this.request<TestCase>('PATCH', `/testing/cases/${id}`, input);\n }\n\n async deleteCase(id: string): Promise<void> {\n await this.request<unknown>('DELETE', `/testing/cases/${id}`);\n }\n\n // ── Test Environments ──\n\n async createEnvironment(input: CreateTestEnvironmentInput): Promise<TestEnvironment> {\n return this.request<TestEnvironment>('POST', '/testing/environments', input);\n }\n\n async listEnvironments(): Promise<TestEnvironment[]> {\n return this.request<TestEnvironment[]>('GET', '/testing/environments');\n }\n\n async getEnvironment(id: string): Promise<TestEnvironment> {\n return this.request<TestEnvironment>('GET', `/testing/environments/${id}`);\n }\n\n async updateEnvironment(id: string, input: UpdateTestEnvironmentInput): Promise<TestEnvironment> {\n return this.request<TestEnvironment>('PATCH', `/testing/environments/${id}`, input);\n }\n\n // ── Deployments ──\n\n async createDeployment(input: CreateDeploymentInput): Promise<Deployment> {\n return this.request<Deployment>('POST', '/testing/deployments', input);\n }\n\n async listDeployments(filters?: ListDeploymentsFilters): Promise<PaginatedResponse<Deployment>> {\n const qs = this.buildQueryString(filters as Record<string, string | number | undefined>);\n return this.request<PaginatedResponse<Deployment>>('GET', `/testing/deployments${qs}`);\n }\n\n async getDeployment(id: string): Promise<Deployment> {\n return this.request<Deployment>('GET', `/testing/deployments/${id}`);\n }\n\n async updateDeployment(id: string, input: UpdateDeploymentInput): Promise<Deployment> {\n return this.request<Deployment>('PATCH', `/testing/deployments/${id}`, input);\n }\n\n async deleteDeployment(id: string): Promise<void> {\n await this.request<unknown>('DELETE', `/testing/deployments/${id}`);\n }\n\n // ── High-Level: Report ──\n\n /**\n * High-level method that creates a test run, submits CTRF results, and returns the updated run.\n */\n async report(\n ctrfFilePath: string,\n options?: {\n name?: string;\n environment?: string;\n deploymentId?: string;\n tool?: string;\n buildName?: string;\n buildUrl?: string;\n },\n ): Promise<TestRun> {\n const raw = readFileSync(ctrfFilePath, 'utf-8');\n const ctrf = JSON.parse(raw) as CtrfReport;\n\n const run = await this.createRun({\n name: options?.name ?? ctrfFilePath,\n environment: options?.environment,\n deploymentId: options?.deploymentId,\n tool: options?.tool ?? ctrf.results.tool?.name,\n buildName: options?.buildName,\n buildUrl: options?.buildUrl,\n });\n\n try {\n await this.submitResults(run.id, ctrf);\n } catch (err) {\n await this.updateRun(run.id, {\n status: 'errored',\n completedAt: new Date().toISOString(),\n });\n throw err;\n }\n\n return this.getRun(run.id);\n }\n}\n"],"mappings":";AAIA,SAAS,oBAAoB;AAwB7B,IAAM,kBAAkB;AACxB,IAAM,mBAAmB;AAElB,IAAM,oBAAN,MAAwB;AAAA,EACnB;AAAA,EACA,QAAuB;AAAA,EACvB,iBAAiB;AAAA,EAEzB,YAAY,SAAmC;AAC3C,SAAK,cAAc;AAAA,MACf,UAAU,QAAQ;AAAA,MAClB,cAAc,QAAQ;AAAA,MACtB,OAAO,QAAQ;AAAA,MACf,SAAS,QAAQ,UAAU,iBAAiB,QAAQ,QAAQ,EAAE;AAAA,MAC9D,SAAS,QAAQ,WAAW;AAAA,IAChC;AAAA,EACJ;AAAA;AAAA,EAIA,MAAc,eAAgC;AAC1C,QAAI,KAAK,SAAS,KAAK,IAAI,IAAI,KAAK,iBAAiB,KAAQ;AACzD,aAAO,KAAK;AAAA,IAChB;AAEA,UAAM,WAAW,MAAM,MAAM,KAAK,YAAY,SAAS;AAAA,MACnD,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,MAC/D,MAAM,IAAI,gBAAgB;AAAA,QACtB,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,WAAW,KAAK,YAAY;AAAA,QAC5B,eAAe,KAAK,YAAY;AAAA,MACpC,CAAC;AAAA,IACL,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AACd,YAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,YAAM,IAAI,MAAM,+BAA+B,SAAS,MAAM,GAAG,OAAO,WAAM,IAAI,KAAK,EAAE,EAAE;AAAA,IAC/F;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,QAAI,CAAC,KAAK,cAAc;AACpB,YAAM,IAAI,MAAM,oDAAoD;AAAA,IACxE;AAEA,SAAK,QAAQ,KAAK;AAClB,SAAK,iBAAiB,KAAK,IAAI,KAAK,KAAK,cAAc,QAAQ;AAC/D,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,MAAc,QAAW,QAAgB,MAAc,MAAgB,QAAQ,MAAkB;AAC7F,UAAM,QAAQ,MAAM,KAAK,aAAa;AACtC,UAAM,MAAM,GAAG,KAAK,YAAY,MAAM,kBAAkB,KAAK,YAAY,KAAK,GAAG,IAAI;AAErF,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAC9B;AAAA,MACA,SAAS;AAAA,QACL,eAAe,UAAU,KAAK;AAAA,QAC9B,gBAAgB;AAAA,MACpB;AAAA,MACA,MAAM,QAAQ,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IAChD,CAAC;AAED,QAAI,SAAS,WAAW,OAAO,OAAO;AAClC,WAAK,QAAQ;AACb,WAAK,iBAAiB;AACtB,aAAO,KAAK,QAAW,QAAQ,MAAM,MAAM,KAAK;AAAA,IACpD;AAEA,QAAI,CAAC,SAAS,IAAI;AACd,YAAM,YAAY,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACtD,YAAM,IAAI,MAAM,mBAAmB,SAAS,MAAM,IAAI,SAAS,UAAU,GAAG,YAAY,WAAM,SAAS,KAAK,EAAE,EAAE;AAAA,IACpH;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,QAAI,CAAC,KAAM,QAAO;AAClB,WAAO,KAAK,MAAM,IAAI;AAAA,EAC1B;AAAA,EAEQ,iBAAiB,QAA8D;AACnF,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,KAAK,IAAI,gBAAgB;AAC/B,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC/C,UAAI,SAAS,KAAM,IAAG,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,IAChD;AACA,UAAM,KAAK,GAAG,SAAS;AACvB,WAAO,KAAK,IAAI,EAAE,KAAK;AAAA,EAC3B;AAAA;AAAA,EAIA,MAAM,UAAU,OAA6C;AACzD,WAAO,KAAK,QAAiB,QAAQ,iBAAiB,KAAK;AAAA,EAC/D;AAAA,EAEA,MAAM,SAAS,SAAoE;AAC/E,UAAM,KAAK,KAAK,iBAAiB,OAAsD;AACvF,WAAO,KAAK,QAAoC,OAAO,gBAAgB,EAAE,EAAE;AAAA,EAC/E;AAAA,EAEA,MAAM,OAAO,IAA8B;AACvC,WAAO,KAAK,QAAiB,OAAO,iBAAiB,EAAE,EAAE;AAAA,EAC7D;AAAA,EAEA,MAAM,UAAU,IAAY,OAA6C;AACrE,WAAO,KAAK,QAAiB,SAAS,iBAAiB,EAAE,IAAI,KAAK;AAAA,EACtE;AAAA,EAEA,MAAM,cAAc,OAAe,MAA8C;AAC7E,WAAO,KAAK,QAA2B,QAAQ,iBAAiB,KAAK,YAAY,IAAI;AAAA,EACzF;AAAA;AAAA,EAIA,MAAM,WAAW,OAA+C;AAC5D,WAAO,KAAK,QAAkB,QAAQ,kBAAkB,KAAK;AAAA,EACjE;AAAA,EAEA,MAAM,UAAU,SAAsE;AAClF,UAAM,KAAK,KAAK,iBAAiB,OAAsD;AACvF,WAAO,KAAK,QAAqC,OAAO,iBAAiB,EAAE,EAAE;AAAA,EACjF;AAAA,EAEA,MAAM,QAAQ,IAA+B;AACzC,WAAO,KAAK,QAAkB,OAAO,kBAAkB,EAAE,EAAE;AAAA,EAC/D;AAAA,EAEA,MAAM,WAAW,IAAY,OAA+C;AACxE,WAAO,KAAK,QAAkB,SAAS,kBAAkB,EAAE,IAAI,KAAK;AAAA,EACxE;AAAA,EAEA,MAAM,WAAW,IAA2B;AACxC,UAAM,KAAK,QAAiB,UAAU,kBAAkB,EAAE,EAAE;AAAA,EAChE;AAAA;AAAA,EAIA,MAAM,kBAAkB,OAA6D;AACjF,WAAO,KAAK,QAAyB,QAAQ,yBAAyB,KAAK;AAAA,EAC/E;AAAA,EAEA,MAAM,mBAA+C;AACjD,WAAO,KAAK,QAA2B,OAAO,uBAAuB;AAAA,EACzE;AAAA,EAEA,MAAM,eAAe,IAAsC;AACvD,WAAO,KAAK,QAAyB,OAAO,yBAAyB,EAAE,EAAE;AAAA,EAC7E;AAAA,EAEA,MAAM,kBAAkB,IAAY,OAA6D;AAC7F,WAAO,KAAK,QAAyB,SAAS,yBAAyB,EAAE,IAAI,KAAK;AAAA,EACtF;AAAA;AAAA,EAIA,MAAM,iBAAiB,OAAmD;AACtE,WAAO,KAAK,QAAoB,QAAQ,wBAAwB,KAAK;AAAA,EACzE;AAAA,EAEA,MAAM,gBAAgB,SAA0E;AAC5F,UAAM,KAAK,KAAK,iBAAiB,OAAsD;AACvF,WAAO,KAAK,QAAuC,OAAO,uBAAuB,EAAE,EAAE;AAAA,EACzF;AAAA,EAEA,MAAM,cAAc,IAAiC;AACjD,WAAO,KAAK,QAAoB,OAAO,wBAAwB,EAAE,EAAE;AAAA,EACvE;AAAA,EAEA,MAAM,iBAAiB,IAAY,OAAmD;AAClF,WAAO,KAAK,QAAoB,SAAS,wBAAwB,EAAE,IAAI,KAAK;AAAA,EAChF;AAAA,EAEA,MAAM,iBAAiB,IAA2B;AAC9C,UAAM,KAAK,QAAiB,UAAU,wBAAwB,EAAE,EAAE;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OACF,cACA,SAQgB;AAChB,UAAM,MAAM,aAAa,cAAc,OAAO;AAC9C,UAAM,OAAO,KAAK,MAAM,GAAG;AAE3B,UAAM,MAAM,MAAM,KAAK,UAAU;AAAA,MAC7B,MAAM,SAAS,QAAQ;AAAA,MACvB,aAAa,SAAS;AAAA,MACtB,cAAc,SAAS;AAAA,MACvB,MAAM,SAAS,QAAQ,KAAK,QAAQ,MAAM;AAAA,MAC1C,WAAW,SAAS;AAAA,MACpB,UAAU,SAAS;AAAA,IACvB,CAAC;AAED,QAAI;AACA,YAAM,KAAK,cAAc,IAAI,IAAI,IAAI;AAAA,IACzC,SAAS,KAAK;AACV,YAAM,KAAK,UAAU,IAAI,IAAI;AAAA,QACzB,QAAQ;AAAA,QACR,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,MACxC,CAAC;AACD,YAAM;AAAA,IACV;AAEA,WAAO,KAAK,OAAO,IAAI,EAAE;AAAA,EAC7B;AACJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/index.ts"],"sourcesContent":["/**\n * SmooTestingClient — programmatic library for the Smoo AI Testing API.\n */\n\nimport { readFileSync } from 'fs';\nimport type {\n CtrfReport,\n CreateDeploymentInput,\n CreateTestCaseInput,\n CreateTestEnvironmentInput,\n CreateTestRunInput,\n Credentials,\n Deployment,\n ListDeploymentsFilters,\n ListTestCasesFilters,\n ListTestRunsFilters,\n PaginatedResponse,\n SmooTestingClientOptions,\n TestCase,\n TestEnvironment,\n TestRun,\n TokenResponse,\n UpdateDeploymentInput,\n UpdateTestCaseInput,\n UpdateTestEnvironmentInput,\n UpdateTestRunInput,\n} from './types';\n\nconst DEFAULT_API_URL = 'https://api.production.smoo.ai';\nconst DEFAULT_AUTH_URL = 'https://auth.production.smoo.ai/token';\n\nexport class SmooTestingClient {\n private credentials: Credentials;\n private token: string | null = null;\n private tokenExpiresAt = 0;\n\n constructor(options: SmooTestingClientOptions) {\n this.credentials = {\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n orgId: options.orgId,\n apiUrl: (options.apiUrl ?? DEFAULT_API_URL).replace(/\\/+$/, ''),\n authUrl: options.authUrl ?? DEFAULT_AUTH_URL,\n };\n }\n\n // ── Auth ──\n\n private async authenticate(): Promise<string> {\n if (this.token && Date.now() < this.tokenExpiresAt - 60_000) {\n return this.token;\n }\n\n const response = await fetch(this.credentials.authUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'client_credentials',\n provider: 'client_credentials',\n client_id: this.credentials.clientId,\n client_secret: this.credentials.clientSecret,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n throw new Error(`Authentication failed: HTTP ${response.status}${body ? ` — ${body}` : ''}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n if (!data.access_token) {\n throw new Error('Authentication failed: no access_token in response');\n }\n\n this.token = data.access_token;\n this.tokenExpiresAt = Date.now() + (data.expires_in || 3600) * 1000;\n return this.token;\n }\n\n private async request<T>(method: string, path: string, body?: unknown, retry = true): Promise<T> {\n const token = await this.authenticate();\n const url = `${this.credentials.apiUrl}/organizations/${this.credentials.orgId}${path}`;\n\n const response = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: body != null ? JSON.stringify(body) : undefined,\n });\n\n if (response.status === 401 && retry) {\n this.token = null;\n this.tokenExpiresAt = 0;\n return this.request<T>(method, path, body, false);\n }\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new Error(`API error: HTTP ${response.status} ${response.statusText}${errorBody ? ` — ${errorBody}` : ''}`);\n }\n\n const text = await response.text();\n if (!text) return undefined as T;\n return JSON.parse(text) as T;\n }\n\n private buildQueryString(params?: Record<string, string | number | undefined>): string {\n if (!params) return '';\n const sp = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value != null) sp.set(key, String(value));\n }\n const qs = sp.toString();\n return qs ? `?${qs}` : '';\n }\n\n // ── Test Runs ──\n\n async createRun(input: CreateTestRunInput): Promise<TestRun> {\n return this.request<TestRun>('POST', '/testing/runs', input);\n }\n\n async listRuns(filters?: ListTestRunsFilters): Promise<PaginatedResponse<TestRun>> {\n const qs = this.buildQueryString(filters as Record<string, string | number | undefined>);\n return this.request<PaginatedResponse<TestRun>>('GET', `/testing/runs${qs}`);\n }\n\n async getRun(id: string): Promise<TestRun> {\n return this.request<TestRun>('GET', `/testing/runs/${id}`);\n }\n\n async updateRun(id: string, input: UpdateTestRunInput): Promise<TestRun> {\n return this.request<TestRun>('PATCH', `/testing/runs/${id}`, input);\n }\n\n async submitResults(runId: string, ctrf: CtrfReport): Promise<{ count: number }> {\n return this.request<{ count: number }>('POST', `/testing/runs/${runId}/results`, ctrf);\n }\n\n // ── Test Cases ──\n\n async createCase(input: CreateTestCaseInput): Promise<TestCase> {\n return this.request<TestCase>('POST', '/testing/cases', input);\n }\n\n async listCases(filters?: ListTestCasesFilters): Promise<PaginatedResponse<TestCase>> {\n const qs = this.buildQueryString(filters as Record<string, string | number | undefined>);\n return this.request<PaginatedResponse<TestCase>>('GET', `/testing/cases${qs}`);\n }\n\n async getCase(id: string): Promise<TestCase> {\n return this.request<TestCase>('GET', `/testing/cases/${id}`);\n }\n\n async updateCase(id: string, input: UpdateTestCaseInput): Promise<TestCase> {\n return this.request<TestCase>('PATCH', `/testing/cases/${id}`, input);\n }\n\n async deleteCase(id: string): Promise<void> {\n await this.request<unknown>('DELETE', `/testing/cases/${id}`);\n }\n\n // ── Test Environments ──\n\n async createEnvironment(input: CreateTestEnvironmentInput): Promise<TestEnvironment> {\n return this.request<TestEnvironment>('POST', '/testing/environments', input);\n }\n\n async listEnvironments(): Promise<TestEnvironment[]> {\n return this.request<TestEnvironment[]>('GET', '/testing/environments');\n }\n\n async getEnvironment(id: string): Promise<TestEnvironment> {\n return this.request<TestEnvironment>('GET', `/testing/environments/${id}`);\n }\n\n async updateEnvironment(id: string, input: UpdateTestEnvironmentInput): Promise<TestEnvironment> {\n return this.request<TestEnvironment>('PATCH', `/testing/environments/${id}`, input);\n }\n\n // ── Deployments ──\n\n async createDeployment(input: CreateDeploymentInput): Promise<Deployment> {\n return this.request<Deployment>('POST', '/testing/deployments', input);\n }\n\n async listDeployments(filters?: ListDeploymentsFilters): Promise<PaginatedResponse<Deployment>> {\n const qs = this.buildQueryString(filters as Record<string, string | number | undefined>);\n return this.request<PaginatedResponse<Deployment>>('GET', `/testing/deployments${qs}`);\n }\n\n async getDeployment(id: string): Promise<Deployment> {\n return this.request<Deployment>('GET', `/testing/deployments/${id}`);\n }\n\n async updateDeployment(id: string, input: UpdateDeploymentInput): Promise<Deployment> {\n return this.request<Deployment>('PATCH', `/testing/deployments/${id}`, input);\n }\n\n async deleteDeployment(id: string): Promise<void> {\n await this.request<unknown>('DELETE', `/testing/deployments/${id}`);\n }\n\n // ── High-Level: Report ──\n\n /**\n * High-level method that creates a test run, submits CTRF results, and returns the updated run.\n */\n async report(\n ctrfFilePath: string,\n options?: {\n name?: string;\n environment?: string;\n deploymentId?: string;\n tool?: string;\n tags?: string[];\n buildName?: string;\n buildUrl?: string;\n },\n ): Promise<TestRun> {\n const raw = readFileSync(ctrfFilePath, 'utf-8');\n const ctrf = JSON.parse(raw) as CtrfReport;\n\n const run = await this.createRun({\n name: options?.name ?? ctrfFilePath,\n environment: options?.environment,\n deploymentId: options?.deploymentId,\n tool: options?.tool ?? ctrf.results.tool?.name,\n tags: options?.tags,\n buildName: options?.buildName,\n buildUrl: options?.buildUrl,\n });\n\n try {\n await this.submitResults(run.id, ctrf);\n } catch (err) {\n await this.updateRun(run.id, {\n status: 'errored',\n completedAt: new Date().toISOString(),\n });\n throw err;\n }\n\n return this.getRun(run.id);\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/lib/types.d.mts
CHANGED
|
@@ -26,6 +26,7 @@ interface TestRun {
|
|
|
26
26
|
deploymentId: string | null;
|
|
27
27
|
name: string;
|
|
28
28
|
tool: string | null;
|
|
29
|
+
tags: string[] | null;
|
|
29
30
|
status: string;
|
|
30
31
|
summary: TestRunSummary | null;
|
|
31
32
|
durationMs: number | null;
|
|
@@ -45,6 +46,7 @@ interface CreateTestRunInput {
|
|
|
45
46
|
environmentId?: string;
|
|
46
47
|
deploymentId?: string;
|
|
47
48
|
tool?: string;
|
|
49
|
+
tags?: string[];
|
|
48
50
|
buildName?: string;
|
|
49
51
|
buildUrl?: string;
|
|
50
52
|
runnerName?: string;
|
|
@@ -57,6 +59,7 @@ interface UpdateTestRunInput {
|
|
|
57
59
|
completedAt?: string;
|
|
58
60
|
startedAt?: string;
|
|
59
61
|
tool?: string;
|
|
62
|
+
tags?: string[];
|
|
60
63
|
metadata?: Record<string, unknown>;
|
|
61
64
|
}
|
|
62
65
|
interface ListTestRunsFilters {
|
|
@@ -65,6 +68,7 @@ interface ListTestRunsFilters {
|
|
|
65
68
|
status?: string;
|
|
66
69
|
environmentId?: string;
|
|
67
70
|
tool?: string;
|
|
71
|
+
tags?: string;
|
|
68
72
|
runnerName?: string;
|
|
69
73
|
startDate?: string;
|
|
70
74
|
endDate?: string;
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ interface TestRun {
|
|
|
26
26
|
deploymentId: string | null;
|
|
27
27
|
name: string;
|
|
28
28
|
tool: string | null;
|
|
29
|
+
tags: string[] | null;
|
|
29
30
|
status: string;
|
|
30
31
|
summary: TestRunSummary | null;
|
|
31
32
|
durationMs: number | null;
|
|
@@ -45,6 +46,7 @@ interface CreateTestRunInput {
|
|
|
45
46
|
environmentId?: string;
|
|
46
47
|
deploymentId?: string;
|
|
47
48
|
tool?: string;
|
|
49
|
+
tags?: string[];
|
|
48
50
|
buildName?: string;
|
|
49
51
|
buildUrl?: string;
|
|
50
52
|
runnerName?: string;
|
|
@@ -57,6 +59,7 @@ interface UpdateTestRunInput {
|
|
|
57
59
|
completedAt?: string;
|
|
58
60
|
startedAt?: string;
|
|
59
61
|
tool?: string;
|
|
62
|
+
tags?: string[];
|
|
60
63
|
metadata?: Record<string, unknown>;
|
|
61
64
|
}
|
|
62
65
|
interface ListTestRunsFilters {
|
|
@@ -65,6 +68,7 @@ interface ListTestRunsFilters {
|
|
|
65
68
|
status?: string;
|
|
66
69
|
environmentId?: string;
|
|
67
70
|
tool?: string;
|
|
71
|
+
tags?: string;
|
|
68
72
|
runnerName?: string;
|
|
69
73
|
startDate?: string;
|
|
70
74
|
endDate?: string;
|
package/dist/lib/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/types.ts"],"sourcesContent":["/**\n * TypeScript types matching the Smoo AI Testing API schemas.\n */\n\n// ── Test Runs ──\n\nexport interface TestRunSummary {\n total?: number;\n passed?: number;\n failed?: number;\n skipped?: number;\n pending?: number;\n other?: number;\n}\n\nexport interface TestRunDeployment {\n id: string;\n name: string;\n status: string;\n source: string | null;\n externalId: string | null;\n externalUrl: string | null;\n ref: string | null;\n metadata: Record<string, unknown> | null;\n}\n\nexport interface TestRun {\n id: string;\n organizationId: string;\n environmentId: string | null;\n deploymentId: string | null;\n name: string;\n tool: string | null;\n status: string;\n summary: TestRunSummary | null;\n durationMs: number | null;\n runnerName: string | null;\n runnerUrl: string | null;\n startedAt: string | null;\n completedAt: string | null;\n metadata: Record<string, unknown> | null;\n createdAt: string;\n updatedAt: string;\n deployment?: TestRunDeployment | null;\n results?: TestResult[];\n}\n\nexport interface CreateTestRunInput {\n name: string;\n environment?: string;\n environmentId?: string;\n deploymentId?: string;\n tool?: string;\n buildName?: string;\n buildUrl?: string;\n runnerName?: string;\n runnerUrl?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface UpdateTestRunInput {\n status?: string;\n summary?: TestRunSummary;\n completedAt?: string;\n startedAt?: string;\n tool?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface ListTestRunsFilters {\n limit?: number;\n offset?: number;\n status?: string;\n environmentId?: string;\n tool?: string;\n runnerName?: string;\n startDate?: string;\n endDate?: string;\n}\n\n// ── Test Results ──\n\nexport interface TestResult {\n id?: string;\n name: string;\n suite?: string;\n status: string;\n durationMs?: number;\n message?: string;\n trace?: string;\n retryCount?: number;\n flaky?: boolean;\n browser?: string;\n tags?: string[];\n metadata?: Record<string, unknown>;\n}\n\n// ── Test Cases ──\n\nexport interface TestCaseStep {\n id?: string;\n stepNumber: number;\n action: string;\n expectedResult?: string;\n data?: string;\n}\n\nexport interface TestCase {\n id: string;\n organizationId: string;\n title: string;\n description: string | null;\n preconditions: string | null;\n expectedResult: string | null;\n priority: string | null;\n automationStatus: string | null;\n automationId: string | null;\n tags: string[] | null;\n estimatedDurationMs: number | null;\n metadata: Record<string, unknown> | null;\n createdAt: string;\n updatedAt: string;\n steps?: TestCaseStep[];\n recentResults?: TestResult[];\n}\n\nexport interface CreateTestCaseInput {\n title: string;\n description?: string;\n preconditions?: string;\n expectedResult?: string;\n priority?: string;\n automationStatus?: string;\n automationId?: string;\n tags?: string[];\n estimatedDurationMs?: number;\n metadata?: Record<string, unknown>;\n steps?: Omit<TestCaseStep, 'id'>[];\n}\n\nexport interface UpdateTestCaseInput {\n title?: string;\n description?: string;\n preconditions?: string;\n expectedResult?: string;\n priority?: string;\n automationStatus?: string;\n automationId?: string;\n tags?: string[];\n estimatedDurationMs?: number;\n metadata?: Record<string, unknown>;\n steps?: TestCaseStep[];\n}\n\nexport interface ListTestCasesFilters {\n limit?: number;\n offset?: number;\n tags?: string;\n priority?: string;\n automationStatus?: string;\n}\n\n// ── Test Environments ──\n\nexport interface TestEnvironment {\n id: string;\n organizationId: string;\n name: string;\n description: string | null;\n baseUrl: string | null;\n metadata: Record<string, unknown> | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateTestEnvironmentInput {\n name: string;\n description?: string;\n baseUrl?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface UpdateTestEnvironmentInput {\n name?: string;\n description?: string;\n baseUrl?: string;\n metadata?: Record<string, unknown>;\n}\n\n// ── Deployments ──\n\nexport type DeploymentStatus = 'pending' | 'in_progress' | 'success' | 'failure' | 'cancelled';\n\nexport interface Deployment {\n id: string;\n organizationId: string;\n environmentId: string | null;\n name: string;\n status: DeploymentStatus;\n source: string | null;\n externalId: string | null;\n externalUrl: string | null;\n ref: string | null;\n metadata: Record<string, unknown> | null;\n startedAt: string | null;\n completedAt: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateDeploymentInput {\n name: string;\n environmentId?: string;\n status?: DeploymentStatus;\n source?: string;\n externalId?: string;\n externalUrl?: string;\n ref?: string;\n metadata?: Record<string, unknown>;\n startedAt?: string;\n}\n\nexport interface UpdateDeploymentInput {\n name?: string;\n status?: DeploymentStatus;\n source?: string;\n externalId?: string;\n externalUrl?: string;\n ref?: string;\n metadata?: Record<string, unknown>;\n startedAt?: string;\n completedAt?: string;\n}\n\nexport interface ListDeploymentsFilters {\n limit?: number;\n offset?: number;\n status?: string;\n environmentId?: string;\n source?: string;\n startDate?: string;\n endDate?: string;\n}\n\n// ── Pagination ──\n\nexport interface PaginatedResponse<T> {\n data: T[];\n pagination: {\n limit: number;\n offset: number;\n total: number;\n hasMore: boolean;\n };\n}\n\n// ── CTRF (Common Test Report Format) ──\n\nexport interface CtrfReport {\n results: {\n tool?: { name?: string; version?: string };\n summary?: {\n tests?: number;\n passed?: number;\n failed?: number;\n skipped?: number;\n pending?: number;\n other?: number;\n start?: number;\n stop?: number;\n };\n tests?: CtrfTest[];\n environment?: {\n reportName?: string;\n [key: string]: unknown;\n };\n };\n}\n\nexport interface CtrfTest {\n name: string;\n status: 'passed' | 'failed' | 'skipped' | 'pending' | 'other';\n duration?: number;\n suite?: string;\n filePath?: string;\n message?: string;\n trace?: string;\n retries?: number;\n flaky?: boolean;\n browser?: string;\n tags?: string[];\n extra?: Record<string, unknown>;\n}\n\n// ── Auth / Credentials ──\n\nexport interface Credentials {\n clientId: string;\n clientSecret: string;\n orgId: string;\n apiUrl: string;\n authUrl: string;\n}\n\nexport interface TokenResponse {\n access_token: string;\n token_type: string;\n expires_in?: number;\n}\n\n// ── Client Options ──\n\nexport interface SmooTestingClientOptions {\n clientId: string;\n clientSecret: string;\n orgId: string;\n apiUrl?: string;\n authUrl?: string;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/types.ts"],"sourcesContent":["/**\n * TypeScript types matching the Smoo AI Testing API schemas.\n */\n\n// ── Test Runs ──\n\nexport interface TestRunSummary {\n total?: number;\n passed?: number;\n failed?: number;\n skipped?: number;\n pending?: number;\n other?: number;\n}\n\nexport interface TestRunDeployment {\n id: string;\n name: string;\n status: string;\n source: string | null;\n externalId: string | null;\n externalUrl: string | null;\n ref: string | null;\n metadata: Record<string, unknown> | null;\n}\n\nexport interface TestRun {\n id: string;\n organizationId: string;\n environmentId: string | null;\n deploymentId: string | null;\n name: string;\n tool: string | null;\n tags: string[] | null;\n status: string;\n summary: TestRunSummary | null;\n durationMs: number | null;\n runnerName: string | null;\n runnerUrl: string | null;\n startedAt: string | null;\n completedAt: string | null;\n metadata: Record<string, unknown> | null;\n createdAt: string;\n updatedAt: string;\n deployment?: TestRunDeployment | null;\n results?: TestResult[];\n}\n\nexport interface CreateTestRunInput {\n name: string;\n environment?: string;\n environmentId?: string;\n deploymentId?: string;\n tool?: string;\n tags?: string[];\n buildName?: string;\n buildUrl?: string;\n runnerName?: string;\n runnerUrl?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface UpdateTestRunInput {\n status?: string;\n summary?: TestRunSummary;\n completedAt?: string;\n startedAt?: string;\n tool?: string;\n tags?: string[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface ListTestRunsFilters {\n limit?: number;\n offset?: number;\n status?: string;\n environmentId?: string;\n tool?: string;\n tags?: string;\n runnerName?: string;\n startDate?: string;\n endDate?: string;\n}\n\n// ── Test Results ──\n\nexport interface TestResult {\n id?: string;\n name: string;\n suite?: string;\n status: string;\n durationMs?: number;\n message?: string;\n trace?: string;\n retryCount?: number;\n flaky?: boolean;\n browser?: string;\n tags?: string[];\n metadata?: Record<string, unknown>;\n}\n\n// ── Test Cases ──\n\nexport interface TestCaseStep {\n id?: string;\n stepNumber: number;\n action: string;\n expectedResult?: string;\n data?: string;\n}\n\nexport interface TestCase {\n id: string;\n organizationId: string;\n title: string;\n description: string | null;\n preconditions: string | null;\n expectedResult: string | null;\n priority: string | null;\n automationStatus: string | null;\n automationId: string | null;\n tags: string[] | null;\n estimatedDurationMs: number | null;\n metadata: Record<string, unknown> | null;\n createdAt: string;\n updatedAt: string;\n steps?: TestCaseStep[];\n recentResults?: TestResult[];\n}\n\nexport interface CreateTestCaseInput {\n title: string;\n description?: string;\n preconditions?: string;\n expectedResult?: string;\n priority?: string;\n automationStatus?: string;\n automationId?: string;\n tags?: string[];\n estimatedDurationMs?: number;\n metadata?: Record<string, unknown>;\n steps?: Omit<TestCaseStep, 'id'>[];\n}\n\nexport interface UpdateTestCaseInput {\n title?: string;\n description?: string;\n preconditions?: string;\n expectedResult?: string;\n priority?: string;\n automationStatus?: string;\n automationId?: string;\n tags?: string[];\n estimatedDurationMs?: number;\n metadata?: Record<string, unknown>;\n steps?: TestCaseStep[];\n}\n\nexport interface ListTestCasesFilters {\n limit?: number;\n offset?: number;\n tags?: string;\n priority?: string;\n automationStatus?: string;\n}\n\n// ── Test Environments ──\n\nexport interface TestEnvironment {\n id: string;\n organizationId: string;\n name: string;\n description: string | null;\n baseUrl: string | null;\n metadata: Record<string, unknown> | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateTestEnvironmentInput {\n name: string;\n description?: string;\n baseUrl?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface UpdateTestEnvironmentInput {\n name?: string;\n description?: string;\n baseUrl?: string;\n metadata?: Record<string, unknown>;\n}\n\n// ── Deployments ──\n\nexport type DeploymentStatus = 'pending' | 'in_progress' | 'success' | 'failure' | 'cancelled';\n\nexport interface Deployment {\n id: string;\n organizationId: string;\n environmentId: string | null;\n name: string;\n status: DeploymentStatus;\n source: string | null;\n externalId: string | null;\n externalUrl: string | null;\n ref: string | null;\n metadata: Record<string, unknown> | null;\n startedAt: string | null;\n completedAt: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateDeploymentInput {\n name: string;\n environmentId?: string;\n status?: DeploymentStatus;\n source?: string;\n externalId?: string;\n externalUrl?: string;\n ref?: string;\n metadata?: Record<string, unknown>;\n startedAt?: string;\n}\n\nexport interface UpdateDeploymentInput {\n name?: string;\n status?: DeploymentStatus;\n source?: string;\n externalId?: string;\n externalUrl?: string;\n ref?: string;\n metadata?: Record<string, unknown>;\n startedAt?: string;\n completedAt?: string;\n}\n\nexport interface ListDeploymentsFilters {\n limit?: number;\n offset?: number;\n status?: string;\n environmentId?: string;\n source?: string;\n startDate?: string;\n endDate?: string;\n}\n\n// ── Pagination ──\n\nexport interface PaginatedResponse<T> {\n data: T[];\n pagination: {\n limit: number;\n offset: number;\n total: number;\n hasMore: boolean;\n };\n}\n\n// ── CTRF (Common Test Report Format) ──\n\nexport interface CtrfReport {\n results: {\n tool?: { name?: string; version?: string };\n summary?: {\n tests?: number;\n passed?: number;\n failed?: number;\n skipped?: number;\n pending?: number;\n other?: number;\n start?: number;\n stop?: number;\n };\n tests?: CtrfTest[];\n environment?: {\n reportName?: string;\n [key: string]: unknown;\n };\n };\n}\n\nexport interface CtrfTest {\n name: string;\n status: 'passed' | 'failed' | 'skipped' | 'pending' | 'other';\n duration?: number;\n suite?: string;\n filePath?: string;\n message?: string;\n trace?: string;\n retries?: number;\n flaky?: boolean;\n browser?: string;\n tags?: string[];\n extra?: Record<string, unknown>;\n}\n\n// ── Auth / Credentials ──\n\nexport interface Credentials {\n clientId: string;\n clientSecret: string;\n orgId: string;\n apiUrl: string;\n authUrl: string;\n}\n\nexport interface TokenResponse {\n access_token: string;\n token_type: string;\n expires_in?: number;\n}\n\n// ── Client Options ──\n\nexport interface SmooTestingClientOptions {\n clientId: string;\n clientSecret: string;\n orgId: string;\n apiUrl?: string;\n authUrl?: string;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smooai/testing",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Smoo AI Testing SDK — CLI and library for interacting with the Smoo AI Testing API. Report test results, manage test runs, cases, environments, and deployments.",
|
|
5
5
|
"homepage": "https://github.com/SmooAI/testing#readme",
|
|
6
6
|
"bugs": {
|