@smooai/testing 1.0.0 → 1.1.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 CHANGED
@@ -692,6 +692,7 @@ async function runCreate4(options) {
692
692
  if (options.environmentId) body.environmentId = options.environmentId;
693
693
  if (options.deploymentId) body.deploymentId = options.deploymentId;
694
694
  if (options.tool) body.tool = options.tool;
695
+ if (options.tags) body.tags = options.tags.split(",").map((t) => t.trim());
695
696
  if (options.runnerName) body.runnerName = options.runnerName;
696
697
  if (options.runnerUrl) body.runnerUrl = options.runnerUrl;
697
698
  const run = await client.post("/testing/runs", body);
@@ -727,6 +728,7 @@ async function runList4(options) {
727
728
  status: options.status,
728
729
  environmentId: options.environmentId,
729
730
  tool: options.tool,
731
+ tags: options.tags,
730
732
  limit: options.limit,
731
733
  offset: options.offset
732
734
  });
@@ -979,6 +981,7 @@ async function reportLogic(ctrfFile, options) {
979
981
  };
980
982
  if (options.environment) runBody.environment = options.environment;
981
983
  if (options.deploymentId) runBody.deploymentId = options.deploymentId;
984
+ if (options.tags) runBody.tags = options.tags.split(",").map((t) => t.trim());
982
985
  if (options.buildUrl) {
983
986
  runBody.buildUrl = options.buildUrl;
984
987
  } else if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) {
@@ -1029,6 +1032,7 @@ function ReportUI({ ctrfFile, options }) {
1029
1032
  };
1030
1033
  if (options.environment) runBody.environment = options.environment;
1031
1034
  if (options.deploymentId) runBody.deploymentId = options.deploymentId;
1035
+ if (options.tags) runBody.tags = options.tags.split(",").map((t) => t.trim());
1032
1036
  if (options.buildUrl) {
1033
1037
  runBody.buildUrl = options.buildUrl;
1034
1038
  } else if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) {
@@ -1318,11 +1322,11 @@ function createEnvironmentsCommand(program2) {
1318
1322
  // src/cli/commands/runs/index.ts
1319
1323
  function createRunsCommand(program2) {
1320
1324
  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) => {
1325
+ 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
1326
  const { runCreate: runCreate5 } = await Promise.resolve().then(() => (init_create4(), create_exports4));
1323
1327
  runCreate5({ ...opts, json: program2.opts().json ?? opts.json });
1324
1328
  });
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) => {
1329
+ 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
1330
  const { runList: runList5 } = await Promise.resolve().then(() => (init_list4(), list_exports4));
1327
1331
  runList5({ ...opts, json: program2.opts().json ?? opts.json });
1328
1332
  });
@@ -1334,7 +1338,7 @@ function createRunsCommand(program2) {
1334
1338
  const { runUpdate: runUpdate5 } = await Promise.resolve().then(() => (init_update4(), update_exports4));
1335
1339
  runUpdate5(id, { ...opts, json: program2.opts().json ?? opts.json });
1336
1340
  });
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) => {
1341
+ 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)").action(async (ctrfFile, opts) => {
1338
1342
  const { runReport: runReport2 } = await Promise.resolve().then(() => (init_report(), report_exports));
1339
1343
  runReport2(ctrfFile, { ...opts, json: program2.opts().json ?? opts.json });
1340
1344
  });
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 * 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 } 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}\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 if (options.tags) runBody.tags = options.tags.split(',').map((t: string) => t.trim());\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.tags) runBody.tags = options.tags.split(',').map((t: string) => t.trim());\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('--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 .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":";;;;;;;;;;;;AAUA,eAAsB,aAAa,aAA2C;AAE1E,MAAI,eAAe,KAAK,IAAI,IAAI,iBAAiB,KAAQ;AACrD,WAAO;AAAA,EACX;AAEA,QAAM,WAAW,MAAM,MAAM,YAAY,SAAS;AAAA,IAC9C,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,IAC/D,MAAM,IAAI,gBAAgB;AAAA,MACtB,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,WAAW,YAAY;AAAA,MACvB,eAAe,YAAY;AAAA,IAC/B,CAAC;AAAA,EACL,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AACd,UAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,UAAM,IAAI,MAAM,+BAA+B,SAAS,MAAM,GAAG,OAAO,WAAM,IAAI,KAAK,EAAE,EAAE;AAAA,EAC/F;AAEA,QAAM,OAAQ,MAAM,SAAS,KAAK;AAElC,MAAI,CAAC,KAAK,cAAc;AACpB,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACxE;AAEA,gBAAc,KAAK;AAEnB,mBAAiB,KAAK,IAAI,KAAK,KAAK,cAAc,QAAQ;AAE1D,SAAO;AACX;AAEO,SAAS,kBAAwB;AACpC,gBAAc;AACd,mBAAiB;AACrB;AAhDA,IAOI,aACA;AARJ;AAAA;AAAA;AAOA,IAAI,cAA6B;AACjC,IAAI,iBAAiB;AAAA;AAAA;;;ACRrB,IAOa;AAPb;AAAA;AAAA;AAKA;AAEO,IAAM,YAAN,MAAgB;AAAA,MACX;AAAA,MACA;AAAA,MAER,YAAY,aAA0B;AAClC,aAAK,cAAc;AACnB,aAAK,UAAU,YAAY,OAAO,QAAQ,QAAQ,EAAE;AAAA,MACxD;AAAA,MAEQ,QAAQ,MAAsB;AAClC,eAAO,GAAG,KAAK,OAAO,kBAAkB,KAAK,YAAY,KAAK,GAAG,IAAI;AAAA,MACzE;AAAA,MAEA,MAAc,QAAW,QAAgB,KAAa,MAAgB,QAAQ,MAAkB;AAC5F,cAAM,QAAQ,MAAM,aAAa,KAAK,WAAW;AAEjD,cAAM,WAAW,MAAM,MAAM,KAAK;AAAA,UAC9B;AAAA,UACA,SAAS;AAAA,YACL,eAAe,UAAU,KAAK;AAAA,YAC9B,gBAAgB;AAAA,UACpB;AAAA,UACA,MAAM,QAAQ,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,QAChD,CAAC;AAGD,YAAI,SAAS,WAAW,OAAO,OAAO;AAClC,0BAAgB;AAChB,iBAAO,KAAK,QAAW,QAAQ,KAAK,MAAM,KAAK;AAAA,QACnD;AAEA,YAAI,CAAC,SAAS,IAAI;AACd,gBAAM,YAAY,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACtD,gBAAM,IAAI,MAAM,mBAAmB,SAAS,MAAM,IAAI,SAAS,UAAU,GAAG,YAAY,WAAM,SAAS,KAAK,EAAE,EAAE;AAAA,QACpH;AAEA,cAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAI,CAAC,KAAM,QAAO;AAClB,eAAO,KAAK,MAAM,IAAI;AAAA,MAC1B;AAAA,MAEA,MAAM,IAAO,MAAc,QAAkE;AACzF,YAAI,MAAM,KAAK,QAAQ,IAAI;AAC3B,YAAI,QAAQ;AACR,gBAAM,eAAe,IAAI,gBAAgB;AACzC,qBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC/C,gBAAI,SAAS,KAAM,cAAa,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,UAC1D;AACA,gBAAM,KAAK,aAAa,SAAS;AACjC,cAAI,GAAI,QAAO,IAAI,EAAE;AAAA,QACzB;AACA,eAAO,KAAK,QAAW,OAAO,GAAG;AAAA,MACrC;AAAA,MAEA,MAAM,KAAQ,MAAc,MAA4B;AACpD,eAAO,KAAK,QAAW,QAAQ,KAAK,QAAQ,IAAI,GAAG,IAAI;AAAA,MAC3D;AAAA,MAEA,MAAM,MAAS,MAAc,MAA4B;AACrD,eAAO,KAAK,QAAW,SAAS,KAAK,QAAQ,IAAI,GAAG,IAAI;AAAA,MAC5D;AAAA,MAEA,MAAM,OAAU,MAA0B;AACtC,eAAO,KAAK,QAAW,UAAU,KAAK,QAAQ,IAAI,CAAC;AAAA,MACvD;AAAA,IACJ;AAAA;AAAA;;;ACnEA,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,eAAe;AACxB,SAAS,YAAY;AASd,SAAS,kBAAsC;AAElD,QAAM,cAAc,QAAQ,IAAI;AAChC,QAAM,kBAAkB,QAAQ,IAAI;AACpC,QAAM,WAAW,QAAQ,IAAI;AAE7B,MAAI,eAAe,mBAAmB,UAAU;AAC5C,WAAO;AAAA,MACH,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO;AAAA,MACP,QAAQ,QAAQ,IAAI,kBAAkB;AAAA,MACtC,SAAS,QAAQ,IAAI,mBAAmB;AAAA,IAC5C;AAAA,EACJ;AAGA,MAAI;AACA,QAAI,CAAC,WAAW,gBAAgB,EAAG,QAAO;AAC1C,UAAM,MAAM,aAAa,kBAAkB,OAAO;AAClD,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,CAAC,OAAO,YAAY,CAAC,OAAO,gBAAgB,CAAC,OAAO,MAAO,QAAO;AACtE,WAAO;AAAA,MACH,UAAU,OAAO;AAAA,MACjB,cAAc,OAAO;AAAA,MACrB,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO,UAAU;AAAA,MACzB,SAAS,OAAO,WAAW;AAAA,IAC/B;AAAA,EACJ,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEO,SAAS,gBAAgB,aAAgC;AAC5D,YAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAGzC,MAAI,WAAoC,CAAC;AACzC,MAAI;AACA,QAAI,WAAW,gBAAgB,GAAG;AAC9B,iBAAW,KAAK,MAAM,aAAa,kBAAkB,OAAO,CAAC;AAAA,IACjE;AAAA,EACJ,QAAQ;AAAA,EAER;AAEA,QAAM,SAAS,EAAE,GAAG,UAAU,GAAG,YAAY;AAC7C,gBAAc,kBAAkB,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AACpF;AAEO,SAAS,mBAAyB;AACrC,MAAI,CAAC,WAAW,gBAAgB,EAAG;AAEnC,MAAI;AACA,UAAM,WAAW,KAAK,MAAM,aAAa,kBAAkB,OAAO,CAAC;AAEnE,WAAO,SAAS;AAChB,WAAO,SAAS;AAEhB,kBAAc,kBAAkB,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AAAA,EACtF,QAAQ;AAAA,EAER;AACJ;AAEO,SAAS,uBAAoC;AAChD,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,CAAC,OAAO;AACR,YAAQ,MAAM,6HAA6H;AAC3I,YAAQ,KAAK,CAAC;AAAA,EAClB;AACA,SAAO;AACX;AAzFA,IAUM,YACA,kBAEA,iBACA;AAdN;AAAA;AAAA;AAUA,IAAM,aAAa,KAAK,QAAQ,GAAG,SAAS;AAC5C,IAAM,mBAAmB,KAAK,YAAY,kBAAkB;AAE5D,IAAM,kBAAkB;AACxB,IAAM,mBAAmB;AAAA;AAAA;;;ACTlB,SAAS,cAAc,UAA6B;AACvD,MAAI,SAAU,QAAO;AACrB,SAAO,QAAQ,QAAQ,OAAO,KAAK;AACvC;AAEO,SAAS,WAAW,MAAe,WAAW,GAAU;AAC3D,UAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACzC,UAAQ,KAAK,QAAQ;AACzB;AAEO,SAAS,YAAY,SAAiB,SAA0B;AACnE,MAAI,cAAc,GAAG;AACjB,YAAQ,MAAM,UAAU,OAAO,EAAE;AACjC,QAAI,QAAS,SAAQ,MAAM,OAAO;AAClC,YAAQ,KAAK,CAAC;AAAA,EAClB;AACA,aAAW,EAAE,SAAS,OAAO,OAAO,SAAS,QAAQ,GAAG,CAAC;AAC7D;AAtBA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAcA,eAAsB,UAAU,SAAuC;AACnE,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,OAAgC,EAAE,OAAO,QAAQ,MAAM;AAC7D,QAAI,QAAQ,YAAa,MAAK,cAAc,QAAQ;AACpD,QAAI,QAAQ,SAAU,MAAK,WAAW,QAAQ;AAC9C,QAAI,QAAQ,iBAAkB,MAAK,mBAAmB,QAAQ;AAC9D,QAAI,QAAQ,KAAM,MAAK,OAAO,QAAQ,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAEzE,UAAM,KAAK,MAAM,OAAO,KAAe,kBAAkB,IAAI;AAE7D,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,EAAE;AAAA,IACjB;AAEA,YAAQ,IAAI,sBAAsB,GAAG,EAAE,EAAE;AACzC,YAAQ,IAAI,eAAe,GAAG,KAAK,EAAE;AACrC,YAAQ,IAAI,eAAe,GAAG,YAAY,KAAK,EAAE;AAAA,EACrD,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AArCA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA;AAAA;AAAA;AAAA;AAcA,eAAsB,QAAQ,SAAqC;AAC/D,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,SAAS,MAAM,OAAO,IAAiC,kBAAkB;AAAA,MAC3E,MAAM,QAAQ;AAAA,MACd,UAAU,QAAQ;AAAA,MAClB,kBAAkB,QAAQ;AAAA,MAC1B,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,IACpB,CAAC;AAED,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,MAAM;AAAA,IACrB;AAEA,YAAQ,IAAI,eAAe,OAAO,KAAK,MAAM,OAAO,OAAO,WAAW,KAAK;AAAA,CAAM;AACjF,eAAW,MAAM,OAAO,MAAM;AAC1B,YAAM,OAAO,GAAG,MAAM,SAAS,KAAK,GAAG,KAAK,KAAK,IAAI,CAAC,MAAM;AAC5D,cAAQ,IAAI,KAAK,GAAG,EAAE,MAAM,GAAG,YAAY,KAAK,OAAO,CAAC,CAAC,KAAK,GAAG,KAAK,GAAG,IAAI,EAAE;AAAA,IACnF;AAEA,QAAI,OAAO,WAAW,SAAS;AAC3B,cAAQ,IAAI;AAAA,QAAW,OAAO,WAAW,QAAQ,OAAO,KAAK,MAAM,kCAAkC;AAAA,IACzG;AAAA,EACJ,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AA3CA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA;AAAA;AAAA;AAAA;AASA,eAAsB,OAAO,IAAY,SAAoC;AACzE,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,KAAK,MAAM,OAAO,IAAc,kBAAkB,EAAE,EAAE;AAE5D,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,EAAE;AAAA,IACjB;AAEA,YAAQ,IAAI,cAAc,GAAG,EAAE,EAAE;AACjC,YAAQ,IAAI,kBAAkB,GAAG,KAAK,EAAE;AACxC,YAAQ,IAAI,kBAAkB,GAAG,YAAY,KAAK,EAAE;AACpD,YAAQ,IAAI,kBAAkB,GAAG,oBAAoB,KAAK,EAAE;AAC5D,QAAI,GAAG,YAAa,SAAQ,IAAI,kBAAkB,GAAG,WAAW,EAAE;AAClE,QAAI,GAAG,MAAM,OAAQ,SAAQ,IAAI,kBAAkB,GAAG,KAAK,KAAK,IAAI,CAAC,EAAE;AACvE,YAAQ,IAAI,kBAAkB,GAAG,SAAS,EAAE;AAAA,EAChD,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AA9BA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA;AAAA;AAAA;AAAA;AAcA,eAAsB,UAAU,IAAY,SAAuC;AAC/E,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,OAAgC,CAAC;AACvC,QAAI,QAAQ,MAAO,MAAK,QAAQ,QAAQ;AACxC,QAAI,QAAQ,YAAa,MAAK,cAAc,QAAQ;AACpD,QAAI,QAAQ,SAAU,MAAK,WAAW,QAAQ;AAC9C,QAAI,QAAQ,iBAAkB,MAAK,mBAAmB,QAAQ;AAC9D,QAAI,QAAQ,KAAM,MAAK,OAAO,QAAQ,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAEzE,UAAM,KAAK,MAAM,OAAO,MAAgB,kBAAkB,EAAE,IAAI,IAAI;AAEpE,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,EAAE;AAAA,IACjB;AAEA,YAAQ,IAAI,sBAAsB,GAAG,EAAE,EAAE;AACzC,YAAQ,IAAI,YAAY,GAAG,KAAK,EAAE;AAAA,EACtC,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AArCA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA;AAAA;AAAA;AAAA;AAQA,eAAsB,UAAU,IAAY,SAAuC;AAC/E,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,OAAO,OAAO,kBAAkB,EAAE,EAAE;AAE1C,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,EAAE,SAAS,MAAM,GAAG,CAAC;AAAA,IACpC;AAEA,YAAQ,IAAI,sBAAsB,EAAE,EAAE;AAAA,EAC1C,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AAvBA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;;;ACFA,IAAAA,kBAAA;AAAA,SAAAA,iBAAA;AAAA,mBAAAC;AAAA;AAgBA,eAAsBA,WAAU,SAAuC;AACnE,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,OAAgC,EAAE,MAAM,QAAQ,KAAK;AAC3D,QAAI,QAAQ,cAAe,MAAK,gBAAgB,QAAQ;AACxD,QAAI,QAAQ,OAAQ,MAAK,SAAS,QAAQ;AAC1C,QAAI,QAAQ,OAAQ,MAAK,SAAS,QAAQ;AAC1C,QAAI,QAAQ,WAAY,MAAK,aAAa,QAAQ;AAClD,QAAI,QAAQ,YAAa,MAAK,cAAc,QAAQ;AACpD,QAAI,QAAQ,IAAK,MAAK,MAAM,QAAQ;AAEpC,UAAM,aAAa,MAAM,OAAO,KAAiB,wBAAwB,IAAI;AAE7E,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,UAAU;AAAA,IACzB;AAEA,YAAQ,IAAI,uBAAuB,WAAW,EAAE,EAAE;AAClD,YAAQ,IAAI,aAAa,WAAW,IAAI,EAAE;AAC1C,YAAQ,IAAI,aAAa,WAAW,MAAM,EAAE;AAAA,EAChD,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AAzCA,IAAAC,eAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA,IAAAC,gBAAA;AAAA,SAAAA,eAAA;AAAA,iBAAAC;AAAA;AAcA,eAAsBA,SAAQ,SAAqC;AAC/D,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,SAAS,MAAM,OAAO,IAAmC,wBAAwB;AAAA,MACnF,QAAQ,QAAQ;AAAA,MAChB,eAAe,QAAQ;AAAA,MACvB,QAAQ,QAAQ;AAAA,MAChB,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,IACpB,CAAC;AAED,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,MAAM;AAAA,IACrB;AAEA,YAAQ,IAAI,gBAAgB,OAAO,KAAK,MAAM,OAAO,OAAO,WAAW,KAAK;AAAA,CAAM;AAClF,eAAW,KAAK,OAAO,MAAM;AACzB,YAAM,MAAM,EAAE,MAAM,KAAK,EAAE,GAAG,MAAM;AACpC,cAAQ,IAAI,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,GAAG,GAAG,EAAE;AAAA,IACpE;AAEA,QAAI,OAAO,WAAW,SAAS;AAC3B,cAAQ,IAAI;AAAA,QAAW,OAAO,WAAW,QAAQ,OAAO,KAAK,MAAM,kCAAkC;AAAA,IACzG;AAAA,EACJ,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AA3CA,IAAAC,aAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA,IAAAC,eAAA;AAAA,SAAAA,cAAA;AAAA,gBAAAC;AAAA;AASA,eAAsBA,QAAO,IAAY,SAAoC;AACzE,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,aAAa,MAAM,OAAO,IAAgB,wBAAwB,EAAE,EAAE;AAE5E,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,UAAU;AAAA,IACzB;AAEA,YAAQ,IAAI,eAAe,WAAW,EAAE,EAAE;AAC1C,YAAQ,IAAI,mBAAmB,WAAW,IAAI,EAAE;AAChD,YAAQ,IAAI,mBAAmB,WAAW,MAAM,EAAE;AAClD,YAAQ,IAAI,mBAAmB,WAAW,UAAU,KAAK,EAAE;AAC3D,YAAQ,IAAI,mBAAmB,WAAW,OAAO,KAAK,EAAE;AACxD,YAAQ,IAAI,mBAAmB,WAAW,eAAe,KAAK,EAAE;AAChE,YAAQ,IAAI,mBAAmB,WAAW,SAAS,EAAE;AACrD,QAAI,WAAW,aAAa;AACxB,cAAQ,IAAI,mBAAmB,WAAW,WAAW,EAAE;AAAA,IAC3D;AAAA,EACJ,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AAjCA,IAAAC,YAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA,IAAAC,kBAAA;AAAA,SAAAA,iBAAA;AAAA,mBAAAC;AAAA;AAYA,eAAsBA,WAAU,IAAY,SAAuC;AAC/E,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,OAAgC,CAAC;AACvC,QAAI,QAAQ,KAAM,MAAK,OAAO,QAAQ;AACtC,QAAI,QAAQ,OAAQ,MAAK,SAAS,QAAQ;AAC1C,QAAI,QAAQ,YAAa,MAAK,cAAc,QAAQ;AAEpD,UAAM,aAAa,MAAM,OAAO,MAAkB,wBAAwB,EAAE,IAAI,IAAI;AAEpF,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,UAAU;AAAA,IACzB;AAEA,YAAQ,IAAI,uBAAuB,WAAW,EAAE,EAAE;AAClD,YAAQ,IAAI,aAAa,WAAW,MAAM,EAAE;AAAA,EAChD,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AAjCA,IAAAC,eAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA,IAAAC,kBAAA;AAAA,SAAAA,iBAAA;AAAA,mBAAAC;AAAA;AAQA,eAAsBA,WAAU,IAAY,SAAuC;AAC/E,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,OAAO,OAAO,wBAAwB,EAAE,EAAE;AAEhD,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,EAAE,SAAS,MAAM,GAAG,CAAC;AAAA,IACpC;AAEA,YAAQ,IAAI,uBAAuB,EAAE,EAAE;AAAA,EAC3C,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AAvBA,IAAAC,eAAA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;;;ACFA,IAAAC,kBAAA;AAAA,SAAAA,iBAAA;AAAA,mBAAAC;AAAA;AAYA,eAAsBA,WAAU,SAAuC;AACnE,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,OAAgC,EAAE,MAAM,QAAQ,KAAK;AAC3D,QAAI,QAAQ,YAAa,MAAK,cAAc,QAAQ;AACpD,QAAI,QAAQ,QAAS,MAAK,UAAU,QAAQ;AAE5C,UAAM,MAAM,MAAM,OAAO,KAAsB,yBAAyB,IAAI;AAE5E,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,GAAG;AAAA,IAClB;AAEA,YAAQ,IAAI,wBAAwB,IAAI,EAAE,EAAE;AAC5C,YAAQ,IAAI,WAAW,IAAI,IAAI,EAAE;AAAA,EACrC,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AAhCA,IAAAC,eAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA,IAAAC,gBAAA;AAAA,SAAAA,eAAA;AAAA,iBAAAC;AAAA;AASA,eAAsBA,SAAQ,SAAqC;AAC/D,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,OAAO,MAAM,OAAO,IAAuB,uBAAuB;AAExE,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,IAAI;AAAA,IACnB;AAEA,YAAQ,IAAI,sBAAsB,KAAK,MAAM;AAAA,CAAM;AACnD,eAAW,OAAO,MAAM;AACpB,YAAM,MAAM,IAAI,UAAU,KAAK,IAAI,OAAO,MAAM;AAChD,cAAQ,IAAI,KAAK,IAAI,EAAE,KAAK,IAAI,IAAI,GAAG,GAAG,EAAE;AAAA,IAChD;AAAA,EACJ,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AA5BA,IAAAC,aAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA,IAAAC,eAAA;AAAA,SAAAA,cAAA;AAAA,gBAAAC;AAAA;AASA,eAAsBA,QAAO,IAAY,SAAoC;AACzE,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,MAAM,MAAM,OAAO,IAAqB,yBAAyB,EAAE,EAAE;AAE3E,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,GAAG;AAAA,IAClB;AAEA,YAAQ,IAAI,gBAAgB,IAAI,EAAE,EAAE;AACpC,YAAQ,IAAI,kBAAkB,IAAI,IAAI,EAAE;AACxC,YAAQ,IAAI,kBAAkB,IAAI,eAAe,KAAK,EAAE;AACxD,YAAQ,IAAI,kBAAkB,IAAI,WAAW,KAAK,EAAE;AACpD,YAAQ,IAAI,kBAAkB,IAAI,SAAS,EAAE;AAAA,EACjD,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AA5BA,IAAAC,YAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA,IAAAC,kBAAA;AAAA,SAAAA,iBAAA;AAAA,mBAAAC;AAAA;AAYA,eAAsBA,WAAU,IAAY,SAAuC;AAC/E,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,OAAgC,CAAC;AACvC,QAAI,QAAQ,KAAM,MAAK,OAAO,QAAQ;AACtC,QAAI,QAAQ,YAAa,MAAK,cAAc,QAAQ;AACpD,QAAI,QAAQ,QAAS,MAAK,UAAU,QAAQ;AAE5C,UAAM,MAAM,MAAM,OAAO,MAAuB,yBAAyB,EAAE,IAAI,IAAI;AAEnF,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,GAAG;AAAA,IAClB;AAEA,YAAQ,IAAI,wBAAwB,IAAI,EAAE,EAAE;AAC5C,YAAQ,IAAI,WAAW,IAAI,IAAI,EAAE;AAAA,EACrC,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AAjCA,IAAAC,eAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA,IAAAC,kBAAA;AAAA,SAAAA,iBAAA;AAAA,mBAAAC;AAAA;AAiBA,eAAsBA,WAAU,SAAuC;AACnE,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,OAAgC,EAAE,MAAM,QAAQ,KAAK;AAC3D,QAAI,QAAQ,YAAa,MAAK,cAAc,QAAQ;AACpD,QAAI,QAAQ,cAAe,MAAK,gBAAgB,QAAQ;AACxD,QAAI,QAAQ,aAAc,MAAK,eAAe,QAAQ;AACtD,QAAI,QAAQ,KAAM,MAAK,OAAO,QAAQ;AACtC,QAAI,QAAQ,KAAM,MAAK,OAAO,QAAQ,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,MAAc,EAAE,KAAK,CAAC;AACjF,QAAI,QAAQ,WAAY,MAAK,aAAa,QAAQ;AAClD,QAAI,QAAQ,UAAW,MAAK,YAAY,QAAQ;AAEhD,UAAM,MAAM,MAAM,OAAO,KAAc,iBAAiB,IAAI;AAE5D,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,GAAG;AAAA,IAClB;AAEA,YAAQ,IAAI,qBAAqB,IAAI,EAAE,EAAE;AACzC,YAAQ,IAAI,aAAa,IAAI,IAAI,EAAE;AACnC,YAAQ,IAAI,aAAa,IAAI,MAAM,EAAE;AAAA,EACzC,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AA3CA,IAAAC,eAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA,IAAAC,gBAAA;AAAA,SAAAA,eAAA;AAAA,iBAAAC;AAAA;AAeA,eAAsBA,SAAQ,SAAqC;AAC/D,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,SAAS,MAAM,OAAO,IAAgC,iBAAiB;AAAA,MACzE,QAAQ,QAAQ;AAAA,MAChB,eAAe,QAAQ;AAAA,MACvB,MAAM,QAAQ;AAAA,MACd,MAAM,QAAQ;AAAA,MACd,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,IACpB,CAAC;AAED,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,MAAM;AAAA,IACrB;AAEA,YAAQ,IAAI,cAAc,OAAO,KAAK,MAAM,OAAO,OAAO,WAAW,KAAK;AAAA,CAAM;AAChF,eAAW,OAAO,OAAO,MAAM;AAC3B,YAAM,UAAU,IAAI,UAAU,KAAK,IAAI,QAAQ,UAAU,CAAC,KAAK,IAAI,QAAQ,UAAU,CAAC,KAAK,IAAI,QAAQ,WAAW,CAAC,OAAO;AAC1H,cAAQ,IAAI,KAAK,IAAI,EAAE,KAAK,IAAI,OAAO,OAAO,CAAC,CAAC,KAAK,IAAI,IAAI,GAAG,OAAO,EAAE;AAAA,IAC7E;AAEA,QAAI,OAAO,WAAW,SAAS;AAC3B,cAAQ,IAAI;AAAA,QAAW,OAAO,WAAW,QAAQ,OAAO,KAAK,MAAM,kCAAkC;AAAA,IACzG;AAAA,EACJ,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AA7CA,IAAAC,aAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA,IAAAC,eAAA;AAAA,SAAAA,cAAA;AAAA,gBAAAC;AAAA;AASA,eAAsBA,QAAO,IAAY,SAAoC;AACzE,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,MAAM,MAAM,OAAO,IAAa,iBAAiB,EAAE,EAAE;AAE3D,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,GAAG;AAAA,IAClB;AAEA,YAAQ,IAAI,aAAa,IAAI,EAAE,EAAE;AACjC,YAAQ,IAAI,kBAAkB,IAAI,IAAI,EAAE;AACxC,YAAQ,IAAI,kBAAkB,IAAI,MAAM,EAAE;AAC1C,YAAQ,IAAI,kBAAkB,IAAI,QAAQ,KAAK,EAAE;AACjD,QAAI,IAAI,SAAS;AACb,cAAQ,IAAI,kBAAkB,IAAI,QAAQ,UAAU,CAAC,YAAY,IAAI,QAAQ,UAAU,CAAC,YAAY,IAAI,QAAQ,WAAW,CAAC,UAAU;AAAA,IAC1I;AACA,QAAI,IAAI,YAAY;AAChB,cAAQ,IAAI,mBAAmB,IAAI,aAAa,KAAM,QAAQ,CAAC,CAAC,GAAG;AAAA,IACvE;AACA,YAAQ,IAAI,kBAAkB,IAAI,SAAS,EAAE;AAC7C,QAAI,IAAI,aAAa;AACjB,cAAQ,IAAI,kBAAkB,IAAI,WAAW,EAAE;AAAA,IACnD;AACA,QAAI,IAAI,WAAW,IAAI,QAAQ,SAAS,GAAG;AACvC,cAAQ,IAAI,kBAAkB,IAAI,QAAQ,MAAM,UAAU;AAAA,IAC9D;AAAA,EACJ,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AAxCA,IAAAC,YAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA,IAAAC,kBAAA;AAAA,SAAAA,iBAAA;AAAA,mBAAAC;AAAA;AAUA,eAAsBA,WAAU,IAAY,SAAuC;AAC/E,MAAI;AACA,UAAM,QAAQ,qBAAqB;AACnC,UAAM,SAAS,IAAI,UAAU,KAAK;AAElC,UAAM,OAAgC,CAAC;AACvC,QAAI,QAAQ,OAAQ,MAAK,SAAS,QAAQ;AAE1C,UAAM,MAAM,MAAM,OAAO,MAAe,iBAAiB,EAAE,IAAI,IAAI;AAEnE,QAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,iBAAW,GAAG;AAAA,IAClB;AAEA,YAAQ,IAAI,qBAAqB,IAAI,EAAE,EAAE;AACzC,YAAQ,IAAI,aAAa,IAAI,MAAM,EAAE;AAAA,EACzC,SAAS,KAAK;AACV,gBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAChE;AACJ;AA7BA,IAAAC,eAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;;ACHA,SAAS,KAAK,YAAY;AAC1B,OAAO,aAAa;AACpB,OAAO,cAAc;AAKb,SAEQ,KAFR;AAFD,SAAS,OAAO,EAAE,MAAM,GAAsB;AACjD,SACI,qBAAC,OAAI,cAAc,GAAG,eAAc,UAChC;AAAA,wBAAC,YAAS,QAAQ,CAAC,WAAW,SAAS,GACnC,8BAAC,WAAQ,MAAK,WAAU,MAAK,QAAO,GACxC;AAAA,IACA,oBAAC,QAAK,MAAI,MAAE,iBAAM;AAAA,KACtB;AAER;AAdA;AAAA;AAAA;AAAA;AAAA;;;ACAA,SAAS,OAAAC,MAAK,QAAAC,aAAY;AAC1B,OAAO,aAAa;AAcA,SAGY,OAAAC,MAHZ,QAAAC,aAAA;AALb,SAAS,SAAS,EAAE,MAAM,GAA0B;AACvD,SACI,gBAAAD,KAACF,MAAA,EAAI,eAAc,UAAS,WAAW,GAClC,gBAAM,IAAI,CAAC,MAAM,MACd,gBAAAG,MAACH,MAAA,EACG;AAAA,oBAAAG,MAACH,MAAA,EAAI,OAAO,GACP;AAAA,WAAK,WAAW,aACb,gBAAAE,KAACD,OAAA,EAAK,OAAM,UACR,0BAAAC,KAAC,WAAQ,MAAK,QAAO,GACzB;AAAA,MAEH,KAAK,WAAW,UAAU,gBAAAA,KAACD,OAAA,EAAK,OAAM,SAAQ,oBAAC;AAAA,MAC/C,KAAK,WAAW,WAAW,gBAAAC,KAACD,OAAA,EAAK,OAAM,OAAM,oBAAC;AAAA,MAC9C,KAAK,WAAW,aAAa,gBAAAC,KAACD,OAAA,EAAK,OAAM,QAAO,oBAAC;AAAA,OACtD;AAAA,IACA,gBAAAC,KAACD,OAAA,EAAK,OAAO,KAAK,WAAW,UAAU,QAAQ,KAAK,WAAW,SAAS,UAAU,QAAY,eAAK,OAAM;AAAA,IACxG,KAAK,SAAS,gBAAAE,MAACF,OAAA,EAAK,OAAM,OAAM;AAAA;AAAA,MAAI,KAAK;AAAA,OAAM;AAAA,OAZ1C,CAaV,CACH,GACL;AAER;AA/BA;AAAA;AAAA;AAAA;AAAA;;;ACIA,SAAS,gBAAAG,qBAAoB;AAC7B,SAAS,SAAS;AA2CX,SAAS,cAAc,UAA8B;AACxD,MAAI;AACJ,MAAI;AACA,UAAMA,cAAa,UAAU,OAAO;AAAA,EACxC,SAAS,KAAK;AACV,UAAM,IAAI,MAAM,6BAA6B,QAAQ,WAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,EACjH;AAEA,MAAI;AACJ,MAAI;AACA,aAAS,KAAK,MAAM,GAAG;AAAA,EAC3B,QAAQ;AACJ,UAAM,IAAI,MAAM,8BAA8B,QAAQ,EAAE;AAAA,EAC5D;AAEA,QAAM,SAAS,iBAAiB,UAAU,MAAM;AAChD,MAAI,CAAC,OAAO,SAAS;AACjB,UAAM,SAAS,OAAO,MAAM,OAAO,IAAI,CAAC,MAAM,KAAK,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,IAAI;AAC9F,UAAM,IAAI,MAAM;AAAA,EAAyB,MAAM,EAAE;AAAA,EACrD;AAEA,SAAO,OAAO;AAClB;AAEO,SAAS,qBAAqB,QAQnC;AACE,QAAM,UAAU,OAAO,QAAQ;AAC/B,MAAI,SAAS;AACT,UAAM,QAAQ,QAAQ,SAAS;AAC/B,UAAM,SAAS,QAAQ,UAAU;AACjC,UAAM,SAAS,QAAQ,UAAU;AACjC,UAAM,UAAU,QAAQ,WAAW;AACnC,UAAM,UAAU,QAAQ,WAAW;AACnC,UAAM,QAAQ,QAAQ,SAAS;AAC/B,WAAO,EAAE,OAAO,QAAQ,QAAQ,SAAS,SAAS,OAAO,WAAW,SAAS,EAAE;AAAA,EACnF;AAGA,QAAM,QAAQ,OAAO,QAAQ,SAAS,CAAC;AACvC,QAAM,SAAS,EAAE,OAAO,MAAM,QAAQ,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,EAAE;AAC7F,aAAW,QAAQ,OAAO;AACtB,QAAI,KAAK,UAAU,QAAQ;AACvB,aAAO,KAAK,MAA4C;AAAA,IAC5D;AAAA,EACJ;AACA,SAAO,EAAE,GAAG,QAAQ,WAAW,OAAO,SAAS,EAAE;AACrD;AArGA,IAQM,gBAeA;AAvBN;AAAA;AAAA;AAQA,IAAM,iBAAiB,EAAE,OAAO;AAAA,MAC5B,MAAM,EAAE,OAAO;AAAA,MACf,QAAQ,EAAE,KAAK,CAAC,UAAU,UAAU,WAAW,WAAW,OAAO,CAAC;AAAA,MAClE,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,MAC9B,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,MAC3B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,MAC9B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,MAC7B,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,MAC3B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,MAC7B,OAAO,EAAE,QAAQ,EAAE,SAAS;AAAA,MAC5B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,MAC7B,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,MACnC,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,IACtD,CAAC;AAED,IAAM,mBAAmB,EAAE,OAAO;AAAA,MAC9B,SAAS,EAAE,OAAO;AAAA,QACd,MAAM,EACD,OAAO;AAAA,UACJ,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,UAC1B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,QACjC,CAAC,EACA,SAAS;AAAA,QACd,SAAS,EACJ,OAAO;AAAA,UACJ,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,UAC3B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,UAC5B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,UAC5B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,UAC7B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,UAC7B,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,UAC3B,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,UAC3B,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,QAC9B,CAAC,EACA,SAAS;AAAA,QACd,OAAO,EAAE,MAAM,cAAc,EAAE,SAAS;AAAA,QACxC,aAAa,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,MAC5D,CAAC;AAAA,IACL,CAAC;AAAA;AAAA;;;AC9CD;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,gBAAgB;AACzB,SAAS,QAAQ,OAAAC,MAAK,QAAAC,aAAY;AAClC,SAAgB,WAAW,gBAAgB;AA8I/B,gBAAAC,MAIQ,QAAAC,aAJR;AA1HZ,eAAsB,YAClB,UACA,SAKD;AAEC,QAAM,SAAS,cAAc,QAAQ;AACrC,QAAM,UAAU,qBAAqB,MAAM;AAG3C,QAAM,QAAQ,qBAAqB;AACnC,QAAM,SAAS,IAAI,UAAU,KAAK;AAGlC,QAAM,UAAU,QAAQ,QAAQ,SAAS,UAAU,OAAO;AAC1D,QAAM,UAAmC;AAAA,IACrC,MAAM;AAAA,IACN,MAAM,QAAQ,QAAQ,OAAO,QAAQ,MAAM;AAAA,IAC3C,WAAW,QAAQ,aAAa,QAAQ,IAAI;AAAA,EAChD;AAEA,MAAI,QAAQ,YAAa,SAAQ,cAAc,QAAQ;AACvD,MAAI,QAAQ,aAAc,SAAQ,eAAe,QAAQ;AACzD,MAAI,QAAQ,KAAM,SAAQ,OAAO,QAAQ,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,MAAc,EAAE,KAAK,CAAC;AAGpF,MAAI,QAAQ,UAAU;AAClB,YAAQ,WAAW,QAAQ;AAAA,EAC/B,WAAW,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,eAAe;AACpG,YAAQ,WAAW,GAAG,QAAQ,IAAI,iBAAiB,IAAI,QAAQ,IAAI,iBAAiB,iBAAiB,QAAQ,IAAI,aAAa;AAAA,EAClI;AAEA,QAAM,MAAM,MAAM,OAAO,KAAc,iBAAiB,OAAO;AAG/D,MAAI,cAAc;AAClB,MAAI;AACA,UAAM,iBAAiB,MAAM,OAAO,KAAwB,iBAAiB,IAAI,EAAE,YAAY;AAAA,MAC3F,SAAS,OAAO;AAAA,IACpB,CAAC;AACD,kBAAc,eAAe;AAAA,EACjC,SAAS,KAAK;AAEV,UAAM,OAAO,MAAM,iBAAiB,IAAI,EAAE,IAAI;AAAA,MAC1C,QAAQ;AAAA,MACR,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IACxC,CAAC;AACD,UAAM;AAAA,EACV;AAIA,QAAM,aAAa,MAAM,OAAO,IAAa,iBAAiB,IAAI,EAAE,EAAE;AAEtE,SAAO,EAAE,KAAK,YAAY,aAAa,QAAQ;AACnD;AAEA,SAAS,SAAS,EAAE,UAAU,QAAQ,GAAiD;AACnF,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAqB;AAAA,IAC3C,EAAE,OAAO,uBAAuB,QAAQ,UAAU;AAAA,IAClD,EAAE,OAAO,kBAAkB,QAAQ,UAAU;AAAA,IAC7C,EAAE,OAAO,qBAAqB,QAAQ,UAAU;AAAA,IAChD,EAAE,OAAO,sBAAsB,QAAQ,UAAU;AAAA,EACrD,CAAC;AACD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAyD,IAAI;AAEzF,YAAU,MAAM;AACZ,KAAC,YAAY;AACT,UAAI;AAEA,iBAAS,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,MAAO,MAAM,IAAI,EAAE,GAAG,MAAM,QAAQ,UAAU,IAAI,IAAK,CAAC;AACrF,cAAM,SAAS,cAAc,QAAQ;AACrC,cAAM,UAAU,qBAAqB,MAAM;AAC3C,iBAAS,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,MAAO,MAAM,IAAI,EAAE,GAAG,MAAM,QAAQ,OAAO,IAAI,IAAK,CAAC;AAGlF,iBAAS,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,MAAO,MAAM,IAAI,EAAE,GAAG,MAAM,QAAQ,UAAU,IAAI,IAAK,CAAC;AACrF,cAAM,QAAQ,qBAAqB;AACnC,cAAM,SAAS,IAAI,UAAU,KAAK;AAClC,iBAAS,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,MAAO,MAAM,IAAI,EAAE,GAAG,MAAM,QAAQ,OAAO,IAAI,IAAK,CAAC;AAGlF,iBAAS,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,MAAO,MAAM,IAAI,EAAE,GAAG,MAAM,QAAQ,UAAU,IAAI,IAAK,CAAC;AACrF,cAAM,UAAU,QAAQ,QAAQ,SAAS,UAAU,OAAO;AAC1D,cAAM,UAAmC;AAAA,UACrC,MAAM;AAAA,UACN,MAAM,QAAQ,QAAQ,OAAO,QAAQ,MAAM;AAAA,UAC3C,WAAW,QAAQ,aAAa,QAAQ,IAAI;AAAA,QAChD;AACA,YAAI,QAAQ,YAAa,SAAQ,cAAc,QAAQ;AACvD,YAAI,QAAQ,aAAc,SAAQ,eAAe,QAAQ;AACzD,YAAI,QAAQ,KAAM,SAAQ,OAAO,QAAQ,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,MAAc,EAAE,KAAK,CAAC;AACpF,YAAI,QAAQ,UAAU;AAClB,kBAAQ,WAAW,QAAQ;AAAA,QAC/B,WAAW,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,eAAe;AACpG,kBAAQ,WAAW,GAAG,QAAQ,IAAI,iBAAiB,IAAI,QAAQ,IAAI,iBAAiB,iBAAiB,QAAQ,IAAI,aAAa;AAAA,QAClI;AACA,cAAM,MAAM,MAAM,OAAO,KAAc,iBAAiB,OAAO;AAC/D,iBAAS,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,MAAO,MAAM,IAAI,EAAE,GAAG,MAAM,QAAQ,OAAO,IAAI,IAAK,CAAC;AAGlF,iBAAS,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,MAAO,MAAM,IAAI,EAAE,GAAG,MAAM,QAAQ,UAAU,IAAI,IAAK,CAAC;AACrF,cAAM,iBAAiB,MAAM,OAAO,KAAwB,iBAAiB,IAAI,EAAE,YAAY;AAAA,UAC3F,SAAS,OAAO;AAAA,QACpB,CAAC;AACD,iBAAS,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,MAAO,MAAM,IAAI,EAAE,GAAG,MAAM,QAAQ,OAAO,IAAI,IAAK,CAAC;AAElF,cAAM,aAAa,MAAM,OAAO,IAAa,iBAAiB,IAAI,EAAE,EAAE;AACtE,kBAAU,EAAE,KAAK,YAAY,aAAa,eAAe,OAAO,QAAQ,CAAC;AAAA,MAC7E,SAAS,KAAK;AACV;AAAA,UAAS,CAAC,MACN,EAAE,IAAI,CAAC,SAAU,KAAK,WAAW,YAAY,EAAE,GAAG,MAAM,QAAQ,SAAS,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE,IAAI,IAAK;AAAA,QAC9I;AAAA,MACJ;AAAA,IACJ,GAAG;AAAA,EACP,GAAG,CAAC,CAAC;AAEL,SACI,gBAAAA,MAACH,MAAA,EAAI,eAAc,UACf;AAAA,oBAAAE,KAAC,UAAO,OAAM,uBAAsB;AAAA,IACpC,gBAAAA,KAAC,YAAS,OAAc;AAAA,IACvB,UACG,gBAAAC,MAACH,MAAA,EAAI,WAAW,GAAG,eAAc,UAC7B;AAAA,sBAAAG,MAACF,OAAA,EAAK,OAAO,OAAO,QAAQ,YAAY,QAAQ,SAAS,MAAI,MACxD;AAAA,eAAO,QAAQ,YAAY,kBAAa;AAAA,QAAW;AAAA,QAAI,OAAO;AAAA,QAAY;AAAA,SAC/E;AAAA,MACA,gBAAAE,MAACF,OAAA,EACI;AAAA,eAAO,QAAQ;AAAA,QAAO;AAAA,QAAU,OAAO,QAAQ;AAAA,QAAO;AAAA,QAAU,OAAO,QAAQ;AAAA,QAAQ;AAAA,SAC5F;AAAA,MACA,gBAAAE,MAACF,OAAA,EAAK,UAAQ,MAAC;AAAA;AAAA,QAAS,OAAO,IAAI;AAAA,SAAG;AAAA,OAC1C;AAAA,KAER;AAER;AAEO,SAAS,UAAU,UAAkB,SAA8B;AACtE,MAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,gBAAY,UAAU,OAAO,EAAE;AAAA,MAC3B,CAAC,WACG,WAAW;AAAA,QACP,SAAS;AAAA,QACT,OAAO,OAAO,IAAI;AAAA,QAClB,QAAQ,OAAO,IAAI;AAAA,QACnB,aAAa,OAAO;AAAA,QACpB,SAAS,OAAO;AAAA,MACpB,CAAC;AAAA,MACL,CAAC,QAAQ;AACL,oBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAChE;AAAA,IACJ;AACA;AAAA,EACJ;AACA,SAAO,gBAAAC,KAAC,YAAS,UAAoB,SAAkB,CAAE;AAC7D;AAnLA;AAAA;AAAA;AAIA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;;;ACTA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,UAAAE,SAAQ,OAAAC,MAAK,QAAAC,aAAY;AAClC,SAAgB,aAAAC,YAAW,YAAAC,iBAAgB;AAwE/B,gBAAAC,MAIQ,QAAAC,aAJR;AApDZ,eAAsB,WAAW,SAAqE;AAClG,QAAM,WAAW,QAAQ;AACzB,QAAM,eAAe,QAAQ;AAC7B,QAAM,QAAQ,QAAQ;AACtB,QAAM,SAAS,QAAQ,UAAUC;AACjC,QAAM,UAAU,QAAQ,WAAWC;AAEnC,MAAI,CAAC,SAAU,OAAM,IAAI,MAAM,8CAA8C;AAC7E,MAAI,CAAC,aAAc,OAAM,IAAI,MAAM,sDAAsD;AACzF,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,iDAAiD;AAE7E,QAAM,cAA2B,EAAE,UAAU,cAAc,OAAO,QAAQ,QAAQ;AAGlF,QAAM,SAAS,IAAI,UAAU,WAAW;AACxC,QAAM,OAAO,IAAe,uBAAuB;AAGnD,kBAAgB,WAAW;AAE3B,SAAO,EAAE,SAAS,MAAM,MAAM;AAClC;AAEA,SAAS,QAAQ,EAAE,QAAQ,GAA8B;AACrD,QAAM,CAAC,OAAO,QAAQ,IAAIJ,UAAqB;AAAA,IAC3C,EAAE,OAAO,0BAA0B,QAAQ,UAAU;AAAA,IACrD,EAAE,OAAO,wCAAwC,QAAQ,UAAU;AAAA,EACvE,CAAC;AACD,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAmC,IAAI;AAEnE,EAAAD,WAAU,MAAM;AACZ,KAAC,YAAY;AACT,eAAS,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,MAAO,MAAM,IAAI,EAAE,GAAG,MAAM,QAAQ,UAAU,IAAI,IAAK,CAAC;AAErF,UAAI;AACA,cAAM,MAAM,MAAM,WAAW,OAAO;AAEpC,iBAAS;AAAA,UACL,EAAE,OAAO,0BAA0B,QAAQ,OAAO;AAAA,UAClD,EAAE,OAAO,wCAAwC,QAAQ,OAAO;AAAA,QACpE,CAAC;AACD,kBAAU,EAAE,OAAO,IAAI,MAAM,CAAC;AAAA,MAClC,SAAS,KAAK;AACV;AAAA,UAAS,CAAC,MACN,EAAE,IAAI,CAAC,SAAU,KAAK,WAAW,YAAY,EAAE,GAAG,MAAM,QAAQ,SAAS,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE,IAAI,IAAK;AAAA,QAC9I;AAAA,MACJ;AAAA,IACJ,GAAG;AAAA,EACP,GAAG,CAAC,CAAC;AAEL,SACI,gBAAAG,MAACL,MAAA,EAAI,eAAc,UACf;AAAA,oBAAAI,KAAC,UAAO,OAAM,SAAQ;AAAA,IACtB,gBAAAA,KAAC,YAAS,OAAc;AAAA,IACvB,UACG,gBAAAA,KAACJ,MAAA,EAAI,WAAW,GACZ,0BAAAK,MAACJ,OAAA,EAAK,OAAM,SAAQ,MAAI,MAAC;AAAA;AAAA,MACkB,OAAO;AAAA,OAClD,GACJ;AAAA,KAER;AAER;AAEO,SAAS,SAAS,SAA6B;AAClD,MAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,eAAW,OAAO,EAAE;AAAA,MAChB,CAAC,WAAW,WAAW,MAAM;AAAA,MAC7B,CAAC,QAAQ,WAAW,EAAE,SAAS,OAAO,OAAO,IAAI,QAAQ,GAAG,CAAC;AAAA,IACjE;AACA;AAAA,EACJ;AACA,EAAAF,QAAO,gBAAAK,KAAC,WAAQ,SAAkB,CAAE;AACxC;AA/FA,IAkBME,kBACAC;AAnBN;AAAA;AAAA;AAGA;AACA;AACA;AACA;AACA;AAWA,IAAMD,mBAAkB;AACxB,IAAMC,oBAAmB;AAAA;AAAA;;;ACnBzB;AAAA;AAAA;AAAA;AAOO,SAAS,UAAU,SAA8B;AACpD,mBAAiB;AAEjB,MAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,eAAW,EAAE,SAAS,MAAM,SAAS,aAAa,CAAC;AAAA,EACvD;AAEA,UAAQ,IAAI,qEAAqE;AACrF;AAfA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;;;ACDA;AAAA;AAAA;AAAA;AAOO,SAAS,UAAU,SAA8B;AACpD,QAAM,QAAQ,gBAAgB;AAE9B,MAAI,CAAC,cAAc,QAAQ,IAAI,GAAG;AAC9B,eAAW;AAAA,MACP,UAAU,CAAC,CAAC;AAAA,MACZ,OAAO,OAAO,SAAS;AAAA,MACvB,QAAQ,OAAO,UAAU;AAAA,MACzB,SAAS,OAAO,WAAW;AAAA,IAC/B,CAAC;AAAA,EACL;AAEA,MAAI,CAAC,OAAO;AACR,YAAQ,IAAI,4DAA4D;AACxE;AAAA,EACJ;AAEA,UAAQ,IAAI,WAAW;AACvB,UAAQ,IAAI,mBAAmB,MAAM,KAAK,EAAE;AAC5C,UAAQ,IAAI,mBAAmB,MAAM,MAAM,EAAE;AAC7C,UAAQ,IAAI,mBAAmB,MAAM,OAAO,EAAE;AAC9C,UAAQ,IAAI,mBAAmB,MAAM,SAAS,MAAM,GAAG,CAAC,CAAC,KAAK;AAClE;AA7BA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;;;ACDA,SAAS,eAAe;;;ACEjB,SAAS,mBAAmBC,UAA2B;AAC1D,QAAM,QAAQA,SAAQ,QAAQ,OAAO,EAAE,YAAY,mBAAmB;AAEtE,QACK,QAAQ,QAAQ,EAChB,YAAY,oBAAoB,EAChC,eAAe,mBAAmB,iBAAiB,EACnD,OAAO,wBAAwB,aAAa,EAC5C,OAAO,yBAAyB,8CAA8C,EAC9E,OAAO,gCAAgC,mBAAmB,EAC1D,OAAO,iBAAiB,sBAAsB,EAC9C,OAAO,OAAO,SAAS;AACpB,UAAM,EAAE,WAAAC,WAAU,IAAI,MAAM;AAC5B,IAAAA,WAAU,EAAE,GAAG,MAAM,MAAMD,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EACjE,CAAC;AAEL,QACK,QAAQ,MAAM,EACd,YAAY,iBAAiB,EAC7B,OAAO,iBAAiB,gCAAgC,EACxD,OAAO,yBAAyB,oBAAoB,EACpD,OAAO,gCAAgC,6BAA6B,EACpE,OAAO,eAAe,eAAe,IAAI,EACzC,OAAO,gBAAgB,UAAU,GAAG,EACpC,OAAO,OAAO,SAAS;AACpB,UAAM,EAAE,SAAAE,SAAQ,IAAI,MAAM;AAC1B,IAAAA,SAAQ,EAAE,GAAG,MAAM,MAAMF,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EAC/D,CAAC;AAEL,QACK,QAAQ,KAAK,EACb,YAAY,uBAAuB,EACnC,SAAS,QAAQ,cAAc,EAC/B,OAAO,OAAO,IAAI,SAAS;AACxB,UAAM,EAAE,QAAAG,QAAO,IAAI,MAAM;AACzB,IAAAA,QAAO,IAAI,EAAE,MAAMH,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EACzD,CAAC;AAEL,QACK,QAAQ,QAAQ,EAChB,YAAY,oBAAoB,EAChC,SAAS,QAAQ,cAAc,EAC/B,OAAO,mBAAmB,WAAW,EACrC,OAAO,wBAAwB,iBAAiB,EAChD,OAAO,yBAAyB,cAAc,EAC9C,OAAO,gCAAgC,uBAAuB,EAC9D,OAAO,iBAAiB,0BAA0B,EAClD,OAAO,OAAO,IAAI,SAAS;AACxB,UAAM,EAAE,WAAAI,WAAU,IAAI,MAAM;AAC5B,IAAAA,WAAU,IAAI,EAAE,GAAG,MAAM,MAAMJ,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EACrE,CAAC;AAEL,QACK,QAAQ,QAAQ,EAChB,YAAY,oBAAoB,EAChC,SAAS,QAAQ,cAAc,EAC/B,OAAO,OAAO,IAAI,SAAS;AACxB,UAAM,EAAE,WAAAK,WAAU,IAAI,MAAM;AAC5B,IAAAA,WAAU,IAAI,EAAE,MAAML,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EAC5D,CAAC;AAEL,SAAO;AACX;;;AC9DO,SAAS,yBAAyBM,UAA2B;AAChE,QAAM,cAAcA,SAAQ,QAAQ,aAAa,EAAE,YAAY,oBAAoB;AAEnF,cACK,QAAQ,QAAQ,EAChB,YAAY,qBAAqB,EACjC,eAAe,iBAAiB,iBAAiB,EACjD,OAAO,yBAAyB,gBAAgB,EAChD,OAAO,qBAAqB,4DAA4D,EACxF,OAAO,qBAAqB,+BAA+B,EAC3D,OAAO,sBAAsB,aAAa,EAC1C,OAAO,wBAAwB,cAAc,EAC7C,OAAO,eAAe,SAAS,EAC/B,OAAO,OAAO,SAAS;AACpB,UAAM,EAAE,WAAAC,WAAU,IAAI,MAAM;AAC5B,IAAAA,WAAU,EAAE,GAAG,MAAM,MAAMD,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EACjE,CAAC;AAEL,cACK,QAAQ,MAAM,EACd,YAAY,kBAAkB,EAC9B,OAAO,qBAAqB,kBAAkB,EAC9C,OAAO,yBAAyB,0BAA0B,EAC1D,OAAO,qBAAqB,kBAAkB,EAC9C,OAAO,eAAe,eAAe,IAAI,EACzC,OAAO,gBAAgB,UAAU,GAAG,EACpC,OAAO,OAAO,SAAS;AACpB,UAAM,EAAE,SAAAE,SAAQ,IAAI,MAAM;AAC1B,IAAAA,SAAQ,EAAE,GAAG,MAAM,MAAMF,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EAC/D,CAAC;AAEL,cACK,QAAQ,KAAK,EACb,YAAY,wBAAwB,EACpC,SAAS,QAAQ,eAAe,EAChC,OAAO,OAAO,IAAI,SAAS;AACxB,UAAM,EAAE,QAAAG,QAAO,IAAI,MAAM;AACzB,IAAAA,QAAO,IAAI,EAAE,MAAMH,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EACzD,CAAC;AAEL,cACK,QAAQ,QAAQ,EAChB,YAAY,qBAAqB,EACjC,SAAS,QAAQ,eAAe,EAChC,OAAO,iBAAiB,UAAU,EAClC,OAAO,qBAAqB,YAAY,EACxC,OAAO,wBAAwB,kBAAkB,EACjD,OAAO,OAAO,IAAI,SAAS;AACxB,UAAM,EAAE,WAAAI,WAAU,IAAI,MAAM;AAC5B,IAAAA,WAAU,IAAI,EAAE,GAAG,MAAM,MAAMJ,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EACrE,CAAC;AAEL,cACK,QAAQ,QAAQ,EAChB,YAAY,qBAAqB,EACjC,SAAS,QAAQ,eAAe,EAChC,OAAO,OAAO,IAAI,SAAS;AACxB,UAAM,EAAE,WAAAK,WAAU,IAAI,MAAM;AAC5B,IAAAA,WAAU,IAAI,EAAE,MAAML,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EAC5D,CAAC;AAEL,SAAO;AACX;;;AC9DO,SAAS,0BAA0BM,UAA2B;AACjE,QAAM,OAAOA,SAAQ,QAAQ,MAAM,EAAE,YAAY,0BAA0B;AAE3E,OAAK,QAAQ,QAAQ,EAChB,YAAY,2BAA2B,EACvC,eAAe,iBAAiB,kBAAkB,EAClD,OAAO,wBAAwB,aAAa,EAC5C,OAAO,oBAAoB,UAAU,EACrC,OAAO,OAAO,SAAS;AACpB,UAAM,EAAE,WAAAC,WAAU,IAAI,MAAM;AAC5B,IAAAA,WAAU,EAAE,GAAG,MAAM,MAAMD,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EACjE,CAAC;AAEL,OAAK,QAAQ,MAAM,EACd,YAAY,wBAAwB,EACpC,OAAO,OAAO,SAAS;AACpB,UAAM,EAAE,SAAAE,SAAQ,IAAI,MAAM;AAC1B,IAAAA,SAAQ,EAAE,MAAMF,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EACtD,CAAC;AAEL,OAAK,QAAQ,KAAK,EACb,YAAY,8BAA8B,EAC1C,SAAS,QAAQ,gBAAgB,EACjC,OAAO,OAAO,IAAI,SAAS;AACxB,UAAM,EAAE,QAAAG,QAAO,IAAI,MAAM;AACzB,IAAAA,QAAO,IAAI,EAAE,MAAMH,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EACzD,CAAC;AAEL,OAAK,QAAQ,QAAQ,EAChB,YAAY,2BAA2B,EACvC,SAAS,QAAQ,gBAAgB,EACjC,OAAO,iBAAiB,UAAU,EAClC,OAAO,wBAAwB,iBAAiB,EAChD,OAAO,oBAAoB,cAAc,EACzC,OAAO,OAAO,IAAI,SAAS;AACxB,UAAM,EAAE,WAAAI,WAAU,IAAI,MAAM;AAC5B,IAAAA,WAAU,IAAI,EAAE,GAAG,MAAM,MAAMJ,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EACrE,CAAC;AAEL,SAAO;AACX;;;ACxCO,SAAS,kBAAkBK,UAA2B;AACzD,QAAM,OAAOA,SAAQ,QAAQ,MAAM,EAAE,YAAY,kBAAkB;AAEnE,OAAK,QAAQ,QAAQ,EAChB,YAAY,mBAAmB,EAC/B,eAAe,iBAAiB,UAAU,EAC1C,OAAO,wBAAwB,2CAA2C,EAC1E,OAAO,yBAAyB,gBAAgB,EAChD,OAAO,wBAAwB,eAAe,EAC9C,OAAO,iBAAiB,sCAAsC,EAC9D,OAAO,iBAAiB,8CAA8C,EACtE,OAAO,wBAAwB,aAAa,EAC5C,OAAO,sBAAsB,YAAY,EACzC,OAAO,OAAO,SAAS;AACpB,UAAM,EAAE,WAAAC,WAAU,IAAI,MAAM;AAC5B,IAAAA,WAAU,EAAE,GAAG,MAAM,MAAMD,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EACjE,CAAC;AAEL,OAAK,QAAQ,MAAM,EACd,YAAY,gBAAgB,EAC5B,OAAO,qBAAqB,kBAAkB,EAC9C,OAAO,yBAAyB,0BAA0B,EAC1D,OAAO,iBAAiB,gBAAgB,EACxC,OAAO,iBAAiB,kCAAkC,EAC1D,OAAO,eAAe,eAAe,IAAI,EACzC,OAAO,gBAAgB,UAAU,GAAG,EACpC,OAAO,OAAO,SAAS;AACpB,UAAM,EAAE,SAAAE,SAAQ,IAAI,MAAM;AAC1B,IAAAA,SAAQ,EAAE,GAAG,MAAM,MAAMF,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EAC/D,CAAC;AAEL,OAAK,QAAQ,KAAK,EACb,YAAY,sBAAsB,EAClC,SAAS,QAAQ,aAAa,EAC9B,OAAO,OAAO,IAAI,SAAS;AACxB,UAAM,EAAE,QAAAG,QAAO,IAAI,MAAM;AACzB,IAAAA,QAAO,IAAI,EAAE,MAAMH,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EACzD,CAAC;AAEL,OAAK,QAAQ,QAAQ,EAChB,YAAY,mBAAmB,EAC/B,SAAS,QAAQ,aAAa,EAC9B,OAAO,qBAAqB,YAAY,EACxC,OAAO,OAAO,IAAI,SAAS;AACxB,UAAM,EAAE,WAAAI,WAAU,IAAI,MAAM;AAC5B,IAAAA,WAAU,IAAI,EAAE,GAAG,MAAM,MAAMJ,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EACrE,CAAC;AAEL,OAAK,QAAQ,QAAQ,EAChB,YAAY,oEAAoE,EAChF,SAAS,eAAe,+BAA+B,EACvD,OAAO,iBAAiB,iCAAiC,EACzD,OAAO,wBAAwB,kBAAkB,EACjD,OAAO,wBAAwB,eAAe,EAC9C,OAAO,iBAAiB,8BAA8B,EACtD,OAAO,iBAAiB,8CAA8C,EACtE,OAAO,uBAAuB,4BAA4B,EAC1D,OAAO,qBAAqB,+BAA+B,EAC3D,OAAO,OAAO,UAAU,SAAS;AAC9B,UAAM,EAAE,WAAAK,WAAU,IAAI,MAAM;AAC5B,IAAAA,WAAU,UAAU,EAAE,GAAG,MAAM,MAAML,SAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAAA,EAC3E,CAAC;AAEL,SAAO;AACX;;;AJ5DA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QAAQ,KAAK,gBAAgB,EAAE,YAAY,mFAA8E,EAAE,QAAQ,OAAO;AAG1I,QAAQ,OAAO,UAAU,2DAA2D;AAGpF,QACK,QAAQ,OAAO,EACf,YAAY,sCAAsC,EAClD,eAAe,oBAAoB,eAAe,EAClD,eAAe,4BAA4B,mBAAmB,EAC9D,eAAe,iBAAiB,iBAAiB,EACjD,OAAO,mBAAmB,gBAAgB,gCAAgC,EAC1E,OAAO,oBAAoB,kBAAkB,uCAAuC,EACpF,OAAO,OAAO,SAAS;AACpB,QAAM,EAAE,UAAAM,UAAS,IAAI,MAAM;AAC3B,EAAAA,UAAS,EAAE,GAAG,MAAM,MAAM,QAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AAChE,CAAC;AAEL,QACK,QAAQ,QAAQ,EAChB,YAAY,2BAA2B,EACvC,OAAO,OAAO,SAAS;AACpB,QAAM,EAAE,WAAAC,WAAU,IAAI,MAAM;AAC5B,EAAAA,WAAU,EAAE,MAAM,QAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AACxD,CAAC;AAEL,QACK,QAAQ,QAAQ,EAChB,YAAY,oCAAoC,EAChD,OAAO,OAAO,SAAS;AACpB,QAAM,EAAE,WAAAC,WAAU,IAAI,MAAM;AAC5B,EAAAA,WAAU,EAAE,MAAM,QAAQ,KAAK,EAAE,QAAQ,KAAK,KAAK,CAAC;AACxD,CAAC;AAGL,kBAAkB,OAAO;AACzB,mBAAmB,OAAO;AAC1B,0BAA0B,OAAO;AACjC,yBAAyB,OAAO;AAEhC,QAAQ,MAAM;","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
@@ -42,6 +42,7 @@ declare class SmooTestingClient {
42
42
  environment?: string;
43
43
  deploymentId?: string;
44
44
  tool?: string;
45
+ tags?: string[];
45
46
  buildName?: string;
46
47
  buildUrl?: string;
47
48
  }): Promise<TestRun>;
package/dist/index.d.ts CHANGED
@@ -42,6 +42,7 @@ declare class SmooTestingClient {
42
42
  environment?: string;
43
43
  deploymentId?: string;
44
44
  tool?: string;
45
+ tags?: string[];
45
46
  buildName?: string;
46
47
  buildUrl?: string;
47
48
  }): Promise<TestRun>;
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
  });
@@ -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":"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","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":[]}
@@ -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;
@@ -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;
@@ -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.0",
3
+ "version": "1.1.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": {