@saptools/cf-events 0.1.3 → 0.2.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/README.md +28 -76
- package/dist/cli.js +214 -35
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +62 -14
- package/dist/index.js +205 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/types.ts","../src/events.ts","../src/format.ts","../src/runtime.ts","../src/api.ts","../src/cf.ts","../src/selector.ts","../src/ssh.ts"],"sourcesContent":["import { readFileSync, realpathSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport process from \"node:process\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { Command } from \"commander\";\n\nimport { parseTypeFilter } from \"./events.js\";\nimport {\n formatCrashReport,\n formatEventLine,\n formatEventsReport,\n formatSshStatusReport,\n formatStatusReport,\n} from \"./format.js\";\nimport { CfEventsRuntime } from \"./runtime.js\";\nimport type { AuditEvent, CfCredentials } from \"./types.js\";\n\nconst DEFAULT_EVENT_LIMIT = 50;\nconst DEFAULT_CRASH_LIMIT = 50;\nconst DEFAULT_SSH_SINCE = \"24h\";\nconst DEFAULT_WATCH_INTERVAL_MS = 15_000;\nconst DEFAULT_WATCH_LOOKBACK = \"2m\";\nconst MIN_WATCH_INTERVAL_MS = 2000;\n\ninterface CommonFlags {\n readonly email?: string;\n readonly password?: string;\n readonly json?: boolean;\n}\n\ninterface EventsFlags extends CommonFlags {\n readonly limit?: number;\n readonly since?: string;\n readonly type?: string;\n}\n\ninterface SshStatusFlags extends CommonFlags {\n readonly since?: string;\n}\n\ninterface CrashesFlags extends CommonFlags {\n readonly limit?: number;\n readonly since?: string;\n}\n\ninterface WatchFlags extends CommonFlags {\n readonly interval?: number;\n readonly lookback?: string;\n readonly type?: string;\n}\n\nfunction resolveCredential(value: string | undefined, envName: string, optionName: string): string {\n const direct = value?.trim();\n if (direct !== undefined && direct.length > 0) {\n return direct;\n }\n const fromEnv = process.env[envName]?.trim();\n if (fromEnv !== undefined && fromEnv.length > 0) {\n return fromEnv;\n }\n throw new Error(`Missing SAP credentials. Pass --${optionName} or set the ${envName} environment variable.`);\n}\n\nfunction buildCredentials(flags: CommonFlags): CfCredentials {\n return {\n email: resolveCredential(flags.email, \"SAP_EMAIL\", \"email\"),\n password: resolveCredential(flags.password, \"SAP_PASSWORD\", \"password\"),\n };\n}\n\nfunction parsePositiveInteger(value: string, optionName: string): number {\n const parsed = Number.parseInt(value, 10);\n if (!Number.isInteger(parsed) || parsed <= 0) {\n throw new Error(`${optionName} must be a positive integer.`);\n }\n return parsed;\n}\n\nfunction appLabel(selector: string): string {\n return (selector.split(\"/\").at(-1) ?? selector).trim();\n}\n\nfunction resolveSelectorArgument(selector: string): string {\n // Bare app name or full path is passed as-is.\n // Bare names are resolved inside resolveSelector using the current CF target (no global snapshot search).\n return selector;\n}\n\nfunction writeOut(text: string): void {\n process.stdout.write(text.endsWith(\"\\n\") ? text : `${text}\\n`);\n}\n\nfunction writeJson(value: unknown): void {\n process.stdout.write(`${JSON.stringify(value, null, 2)}\\n`);\n}\n\nfunction writeJsonLine(value: unknown): void {\n process.stdout.write(`${JSON.stringify(value)}\\n`);\n}\n\nasync function runEvents(selector: string, flags: EventsFlags): Promise<void> {\n const resolvedSelector = resolveSelectorArgument(selector);\n const runtime = new CfEventsRuntime();\n const events = await runtime.fetchEvents(resolvedSelector, buildCredentials(flags), {\n limit: flags.limit ?? DEFAULT_EVENT_LIMIT,\n since: flags.since,\n types: parseTypeFilter(flags.type),\n });\n if (flags.json === true) {\n writeJson(events);\n return;\n }\n writeOut(formatEventsReport(appLabel(resolvedSelector), events, new Date()));\n}\n\nasync function runSshStatus(selector: string, flags: SshStatusFlags): Promise<void> {\n const resolvedSelector = resolveSelectorArgument(selector);\n const runtime = new CfEventsRuntime();\n const status = await runtime.getSshStatus(\n resolvedSelector,\n buildCredentials(flags),\n flags.since ?? DEFAULT_SSH_SINCE,\n );\n if (flags.json === true) {\n writeJson(status);\n return;\n }\n writeOut(formatSshStatusReport(status, new Date()));\n}\n\nasync function runCrashes(selector: string, flags: CrashesFlags): Promise<void> {\n const resolvedSelector = resolveSelectorArgument(selector);\n const runtime = new CfEventsRuntime();\n const summary = await runtime.getCrashes(resolvedSelector, buildCredentials(flags), {\n limit: flags.limit ?? DEFAULT_CRASH_LIMIT,\n since: flags.since,\n });\n if (flags.json === true) {\n writeJson(summary);\n return;\n }\n writeOut(formatCrashReport(summary, new Date()));\n}\n\nasync function runStatus(selector: string, flags: CommonFlags): Promise<void> {\n const resolvedSelector = resolveSelectorArgument(selector);\n const runtime = new CfEventsRuntime();\n const health = await runtime.getStatus(resolvedSelector, buildCredentials(flags));\n if (flags.json === true) {\n writeJson(health);\n return;\n }\n writeOut(formatStatusReport(health, new Date()));\n}\n\nfunction emitWatchEvent(event: AuditEvent, asJson: boolean): void {\n if (asJson) {\n writeJsonLine(event);\n return;\n }\n writeOut(formatEventLine(event, new Date()));\n}\n\nasync function runWatch(selector: string, flags: WatchFlags): Promise<void> {\n const resolvedSelector = resolveSelectorArgument(selector);\n const interval = flags.interval ?? DEFAULT_WATCH_INTERVAL_MS;\n if (interval < MIN_WATCH_INTERVAL_MS) {\n throw new Error(`--interval must be at least ${MIN_WATCH_INTERVAL_MS.toString()}ms.`);\n }\n\n const runtime = new CfEventsRuntime();\n const controller = new AbortController();\n const cleanup = bindTerminationSignals(() => {\n controller.abort();\n });\n const asJson = flags.json === true;\n\n try {\n process.stderr.write(`Watching ${resolvedSelector} for new audit events. Press Ctrl+C to stop.\\n`);\n await runtime.watchEvents(\n resolvedSelector,\n buildCredentials(flags),\n {\n intervalMs: interval,\n lookback: flags.lookback ?? DEFAULT_WATCH_LOOKBACK,\n types: parseTypeFilter(flags.type),\n },\n (event) => {\n emitWatchEvent(event, asJson);\n },\n controller.signal,\n );\n } finally {\n cleanup();\n }\n}\n\nfunction bindTerminationSignals(stop: () => void): () => void {\n const handler = (): void => {\n stop();\n };\n process.once(\"SIGINT\", handler);\n process.once(\"SIGTERM\", handler);\n return () => {\n process.off(\"SIGINT\", handler);\n process.off(\"SIGTERM\", handler);\n };\n}\n\nfunction addCommonOptions(command: Command): Command {\n return command\n .option(\"--email <value>\", \"SAP email (default: SAP_EMAIL env var)\")\n .option(\"--password <value>\", \"SAP password (default: SAP_PASSWORD env var)\")\n .option(\"--json\", \"Emit structured JSON instead of text\", false);\n}\n\nfunction readPackageVersion(): string {\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n const raw = readFileSync(join(here, \"..\", \"package.json\"), \"utf8\");\n const parsed = JSON.parse(raw) as { readonly version?: unknown };\n return typeof parsed.version === \"string\" && parsed.version.length > 0 ? parsed.version : \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\nfunction suppressBrokenPipe(): void {\n for (const stream of [process.stdout, process.stderr]) {\n stream.on(\"error\", (error: NodeJS.ErrnoException): void => {\n if (error.code === \"EPIPE\") {\n process.exit(0);\n }\n throw error;\n });\n }\n}\n\nfunction buildProgram(): Command {\n const program = new Command();\n program\n .name(\"cf-events\")\n .description(\"Inspect SAP BTP Cloud Foundry application audit events and SSH/debug sessions\")\n .version(readPackageVersion(), \"-V, --version\", \"Print the cf-events package version\");\n\n addCommonOptions(\n program\n .command(\"events\")\n .description(\"List recent audit events for an app\")\n .argument(\"<selector>\", \"region/org/space/app or a bare app name\"),\n )\n .option(\n \"--limit <count>\",\n \"Maximum number of events to return\",\n (value: string) => parsePositiveInteger(value, \"--limit\"),\n )\n .option(\"--since <duration>\", \"Only events newer than a duration (e.g. 30m, 6h, 7d)\")\n .option(\"--type <types>\", 'Comma-separated CF event types, or the shorthand \"ssh\"/\"crash\"')\n .action(async (selector: string, flags: EventsFlags): Promise<void> => {\n await runEvents(selector, flags);\n });\n\n addCommonOptions(\n program\n .command(\"ssh-status\")\n .description(\"Show whether SSH is enabled and detect recent SSH/debug sessions\")\n .argument(\"<selector>\", \"region/org/space/app or a bare app name\"),\n )\n .option(\"--since <duration>\", \"Look-back window for SSH events\", DEFAULT_SSH_SINCE)\n .action(async (selector: string, flags: SshStatusFlags): Promise<void> => {\n await runSshStatus(selector, flags);\n });\n\n addCommonOptions(\n program\n .command(\"crashes\")\n .description(\"Summarize recent crash events for an app\")\n .argument(\"<selector>\", \"region/org/space/app or a bare app name\"),\n )\n .option(\n \"--limit <count>\",\n \"Maximum number of crash events to inspect\",\n (value: string) => parsePositiveInteger(value, \"--limit\"),\n )\n .option(\"--since <duration>\", \"Only crashes newer than a duration (e.g. 30m, 6h, 7d)\")\n .action(async (selector: string, flags: CrashesFlags): Promise<void> => {\n await runCrashes(selector, flags);\n });\n\n addCommonOptions(\n program\n .command(\"status\")\n .description(\"Show app health: requested state, instances, SSH flag, and last event\")\n .argument(\"<selector>\", \"region/org/space/app or a bare app name\"),\n ).action(async (selector: string, flags: CommonFlags): Promise<void> => {\n await runStatus(selector, flags);\n });\n\n addCommonOptions(\n program\n .command(\"watch\")\n .description(\"Poll for new audit events and print them as they appear\")\n .argument(\"<selector>\", \"region/org/space/app or a bare app name\"),\n )\n .option(\n \"--interval <ms>\",\n \"Polling interval in milliseconds\",\n (value: string) => parsePositiveInteger(value, \"--interval\"),\n )\n .option(\"--lookback <duration>\", \"Initial look-back window on start (e.g. 2m)\", DEFAULT_WATCH_LOOKBACK)\n .option(\"--type <types>\", 'Comma-separated CF event types, or the shorthand \"ssh\"/\"crash\"')\n .action(async (selector: string, flags: WatchFlags): Promise<void> => {\n await runWatch(selector, flags);\n });\n\n return program;\n}\n\nexport async function main(argv: readonly string[]): Promise<void> {\n const program = buildProgram();\n await program.parseAsync([...argv]);\n}\n\nfunction isMainModule(): boolean {\n const executedPath = process.argv[1];\n if (executedPath === undefined) {\n return false;\n }\n try {\n return realpathSync(executedPath) === fileURLToPath(import.meta.url);\n } catch {\n return resolve(executedPath) === fileURLToPath(import.meta.url);\n }\n}\n\nasync function runCli(): Promise<void> {\n suppressBrokenPipe();\n try {\n await main(process.argv);\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(1);\n }\n}\n\nif (isMainModule()) {\n await runCli();\n}\n","export type RegionKey = string;\n\n/** Cloud Foundry v3 audit event types that target an application. */\nexport const APP_EVENT_TYPES = [\n \"audit.app.create\",\n \"audit.app.update\",\n \"audit.app.delete-request\",\n \"audit.app.start\",\n \"audit.app.stop\",\n \"audit.app.restage\",\n \"audit.app.ssh-authorized\",\n \"audit.app.ssh-unauthorized\",\n \"audit.app.crash\",\n \"audit.app.process.create\",\n \"audit.app.process.scale\",\n \"audit.app.process.update\",\n \"audit.app.process.crash\",\n \"audit.app.process.terminate_instance\",\n \"audit.app.map-route\",\n \"audit.app.unmap-route\",\n \"audit.app.environment_variables.show\",\n] as const;\n\nexport type AppEventType = (typeof APP_EVENT_TYPES)[number];\n\n/** Event types emitted when an SSH/debug connection is authorized or denied. */\nexport const SSH_EVENT_TYPES = [\"audit.app.ssh-authorized\", \"audit.app.ssh-unauthorized\"] as const;\n\n/** Event types emitted when an application instance crashes. */\nexport const CRASH_EVENT_TYPES = [\"audit.app.crash\", \"audit.app.process.crash\"] as const;\n\nexport interface CfCredentials {\n readonly email: string;\n readonly password: string;\n}\n\n/** A fully resolved `region/org/space/app` target. */\nexport interface ResolvedSelector {\n readonly raw: string;\n readonly regionKey: RegionKey;\n readonly apiEndpoint: string;\n readonly orgName: string;\n readonly spaceName: string;\n readonly appName: string;\n}\n\nexport interface CfSessionInput {\n readonly apiEndpoint: string;\n readonly orgName: string;\n readonly spaceName: string;\n readonly credentials: CfCredentials;\n}\n\n/** Handle for a CF CLI session bound to an isolated, ephemeral `CF_HOME`. */\nexport interface CfCliContext {\n readonly cfHomeDir: string;\n}\n\nexport interface CfEntityRef {\n readonly guid: string;\n readonly type: string;\n readonly name: string;\n}\n\nexport interface AuditEvent {\n readonly guid: string;\n readonly type: string;\n readonly createdAt: string;\n readonly updatedAt: string;\n readonly actor: CfEntityRef;\n readonly target: CfEntityRef;\n readonly data: Record<string, unknown>;\n readonly spaceGuid: string | undefined;\n readonly organizationGuid: string | undefined;\n}\n\nexport interface AppSummary {\n readonly guid: string;\n readonly name: string;\n readonly state: string;\n}\n\nexport interface SshEnabled {\n readonly enabled: boolean;\n readonly reason: string;\n}\n\nexport interface ProcessInstanceStat {\n readonly type: string;\n readonly index: number;\n readonly state: string;\n readonly uptimeSeconds: number | undefined;\n readonly cpu: number | undefined;\n readonly memBytes: number | undefined;\n readonly memQuotaBytes: number | undefined;\n readonly diskBytes: number | undefined;\n readonly diskQuotaBytes: number | undefined;\n}\n\nexport interface CrashRecord {\n readonly at: string;\n readonly index: number | undefined;\n readonly reason: string | undefined;\n readonly exitStatus: number | undefined;\n}\n\nexport interface CrashSummary {\n readonly appName: string;\n readonly crashCount: number;\n readonly lastCrashAt: string | undefined;\n readonly lastCrashReason: string | undefined;\n readonly crashes: readonly CrashRecord[];\n}\n\n/**\n * An SSH/debug session inferred from a single `ssh-authorized` audit event.\n * Cloud Foundry does not expose live sessions, so `likelyActive` is a\n * best-effort signal based on how recently the session was authorized.\n */\nexport interface SshSession {\n readonly actor: string;\n readonly authorizedAt: string;\n readonly likelyActive: boolean;\n}\n\nexport interface SshStatus {\n readonly appName: string;\n readonly sshEnabled: boolean;\n readonly sshReason: string;\n readonly sessions: readonly SshSession[];\n readonly deniedAttempts: readonly AuditEvent[];\n readonly activeSessionCount: number;\n}\n\nexport interface AppHealth {\n readonly appName: string;\n readonly appGuid: string;\n readonly requestedState: string;\n readonly sshEnabled: boolean;\n readonly instances: readonly ProcessInstanceStat[];\n readonly lastEvent: AuditEvent | undefined;\n}\n\nexport interface FetchAuditEventsInput {\n readonly appGuid: string;\n readonly types: readonly string[] | undefined;\n readonly createdAfter: string | undefined;\n readonly limit: number;\n}\n","import type { AuditEvent, CrashRecord, CrashSummary } from \"./types.js\";\nimport { CRASH_EVENT_TYPES, SSH_EVENT_TYPES } from \"./types.js\";\n\nconst DURATION_PATTERN = /^(\\d+)\\s*(s|m|h|d)$/;\n\nconst DURATION_UNIT_MS: Readonly<Record<string, number>> = {\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n};\n\nexport function isCrashEvent(event: AuditEvent): boolean {\n return (CRASH_EVENT_TYPES as readonly string[]).includes(event.type);\n}\n\nexport function isSshEvent(event: AuditEvent): boolean {\n return (SSH_EVENT_TYPES as readonly string[]).includes(event.type);\n}\n\nexport function filterByTypes(\n events: readonly AuditEvent[],\n types: readonly string[],\n): readonly AuditEvent[] {\n if (types.length === 0) {\n return events;\n }\n const allowed = new Set(types);\n return events.filter((event) => allowed.has(event.type));\n}\n\nexport function sortEventsNewestFirst(events: readonly AuditEvent[]): readonly AuditEvent[] {\n return [...events].sort((left, right) => right.createdAt.localeCompare(left.createdAt));\n}\n\n/** Parses a human duration such as `30m`, `6h`, or `7d` into milliseconds. */\nexport function parseDuration(raw: string): number {\n const match = DURATION_PATTERN.exec(raw.trim().toLowerCase());\n const amountToken = match?.[1];\n const unitToken = match?.[2];\n if (amountToken === undefined || unitToken === undefined) {\n throw new Error(`Invalid duration \"${raw}\". Use forms like 30m, 6h, or 7d.`);\n }\n\n const amount = Number.parseInt(amountToken, 10);\n const unitMs = DURATION_UNIT_MS[unitToken];\n if (unitMs === undefined || amount <= 0) {\n throw new Error(`Invalid duration \"${raw}\". Use forms like 30m, 6h, or 7d.`);\n }\n return amount * unitMs;\n}\n\n/** Converts a duration into an ISO timestamp suitable for `created_ats[gt]`. */\nexport function durationToCreatedAfter(raw: string, now: Date): string {\n return new Date(now.getTime() - parseDuration(raw)).toISOString();\n}\n\n/**\n * Parses a comma-separated `--type` filter. Accepts the shorthands `ssh` and\n * `crash`, or any full Cloud Foundry event type (e.g. `audit.app.start`).\n */\nexport function parseTypeFilter(raw: string | undefined): readonly string[] {\n if (raw === undefined) {\n return [];\n }\n\n const tokens = raw\n .split(\",\")\n .map((token) => token.trim())\n .filter((token) => token.length > 0);\n\n const resolved: string[] = [];\n for (const token of tokens) {\n if (token === \"ssh\") {\n resolved.push(...SSH_EVENT_TYPES);\n } else if (token === \"crash\") {\n resolved.push(...CRASH_EVENT_TYPES);\n } else if (token.startsWith(\"audit.\") || token.startsWith(\"app.\")) {\n resolved.push(token);\n } else {\n throw new Error(\n `Unknown event type \"${token}\". Use a full CF event type ` +\n '(e.g. audit.app.start) or the shorthand \"ssh\"/\"crash\".',\n );\n }\n }\n return [...new Set(resolved)];\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction readNumber(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nexport function toCrashRecord(event: AuditEvent): CrashRecord {\n return {\n at: event.createdAt,\n index: readNumber(event.data[\"index\"]),\n reason: readString(event.data[\"reason\"]) ?? readString(event.data[\"exit_description\"]),\n exitStatus: readNumber(event.data[\"exit_status\"]),\n };\n}\n\nexport function summarizeCrashes(appName: string, events: readonly AuditEvent[]): CrashSummary {\n const crashes = events\n .filter(isCrashEvent)\n .map(toCrashRecord)\n .sort((left, right) => right.at.localeCompare(left.at));\n const last = crashes[0];\n return {\n appName,\n crashCount: crashes.length,\n lastCrashAt: last?.at,\n lastCrashReason: last?.reason,\n crashes,\n };\n}\n","import type { AppHealth, AuditEvent, CrashSummary, ProcessInstanceStat, SshStatus } from \"./types.js\";\n\nconst EVENT_LABELS: Readonly<Record<string, string>> = {\n \"audit.app.create\": \"App created\",\n \"audit.app.update\": \"App updated\",\n \"audit.app.delete-request\": \"App delete requested\",\n \"audit.app.start\": \"App started\",\n \"audit.app.stop\": \"App stopped\",\n \"audit.app.restage\": \"App restaged\",\n \"audit.app.ssh-authorized\": \"SSH session authorized\",\n \"audit.app.ssh-unauthorized\": \"SSH attempt denied\",\n \"audit.app.crash\": \"App crashed\",\n \"audit.app.process.create\": \"Process created\",\n \"audit.app.process.scale\": \"Process scaled\",\n \"audit.app.process.update\": \"Process updated\",\n \"audit.app.process.crash\": \"Process crashed\",\n \"audit.app.process.terminate_instance\": \"Instance terminated\",\n \"audit.app.map-route\": \"Route mapped\",\n \"audit.app.unmap-route\": \"Route unmapped\",\n \"audit.app.environment_variables.show\": \"Env variables viewed\",\n};\n\nexport function describeEventType(type: string): string {\n return EVENT_LABELS[type] ?? type;\n}\n\nexport function formatUptime(seconds: number | undefined): string {\n if (seconds === undefined || seconds <= 0) {\n return \"-\";\n }\n const totalMinutes = Math.floor(seconds / 60);\n const days = Math.floor(totalMinutes / 1440);\n const hours = Math.floor((totalMinutes % 1440) / 60);\n const minutes = totalMinutes % 60;\n const parts: string[] = [];\n if (days > 0) {\n parts.push(`${days.toString()}d`);\n }\n if (hours > 0) {\n parts.push(`${hours.toString()}h`);\n }\n if (minutes > 0 || parts.length === 0) {\n parts.push(`${minutes.toString()}m`);\n }\n return parts.join(\" \");\n}\n\nexport function formatBytes(bytes: number | undefined): string {\n if (bytes === undefined || bytes < 0) {\n return \"-\";\n }\n const units = [\"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\"];\n let value = bytes;\n let unitIndex = 0;\n while (value >= 1024 && unitIndex < units.length - 1) {\n value /= 1024;\n unitIndex += 1;\n }\n return `${value.toFixed(unitIndex === 0 ? 0 : 1)} ${units[unitIndex] ?? \"B\"}`;\n}\n\nexport function formatCpu(cpu: number | undefined): string {\n if (cpu === undefined) {\n return \"-\";\n }\n return `${(cpu * 100).toFixed(1)}%`;\n}\n\nexport function formatRelativeTime(timestamp: string, now: Date): string {\n const at = Date.parse(timestamp);\n if (Number.isNaN(at)) {\n return \"unknown\";\n }\n const deltaMs = now.getTime() - at;\n if (deltaMs < 60_000) {\n return \"just now\";\n }\n const minutes = Math.floor(deltaMs / 60_000);\n if (minutes < 60) {\n return `${minutes.toString()}m ago`;\n }\n const hours = Math.floor(minutes / 60);\n if (hours < 24) {\n return `${hours.toString()}h ago`;\n }\n return `${Math.floor(hours / 24).toString()}d ago`;\n}\n\nexport function formatEventLine(event: AuditEvent, now: Date): string {\n const actor = event.actor.name.length > 0 ? event.actor.name : event.actor.type;\n return [\n ` ${event.createdAt.padEnd(26)}`,\n formatRelativeTime(event.createdAt, now).padEnd(11),\n describeEventType(event.type).padEnd(24),\n actor.length > 0 ? actor : \"(unknown actor)\",\n ].join(\" \");\n}\n\nexport function formatEventsReport(\n appName: string,\n events: readonly AuditEvent[],\n now: Date,\n): string {\n if (events.length === 0) {\n return `No audit events found for ${appName}.`;\n }\n return [\n `Audit events for ${appName} (${events.length.toString()}):`,\n ...events.map((event) => formatEventLine(event, now)),\n ].join(\"\\n\");\n}\n\nexport function formatSshStatusReport(status: SshStatus, now: Date): string {\n const sessionLines = status.sessions.map((session) => {\n const tag = session.likelyActive ? \"[active]\" : \"[past] \";\n const when = formatRelativeTime(session.authorizedAt, now);\n return ` ${tag} ${session.actor.padEnd(30)} authorized ${session.authorizedAt} (${when})`;\n });\n const deniedLines = status.deniedAttempts.map((attempt) => {\n const actor = attempt.actor.name.length > 0 ? attempt.actor.name : attempt.actor.type;\n return ` ${actor.padEnd(30)} ${attempt.createdAt}`;\n });\n\n return [\n `SSH status for ${status.appName}`,\n \"\",\n ` SSH enabled: ${status.sshEnabled ? \"yes\" : \"no\"}`,\n ...(!status.sshEnabled && status.sshReason.length > 0\n ? [` Disabled reason: ${status.sshReason}`]\n : []),\n ` Recent SSH sessions: ${status.sessions.length.toString()}`,\n ` Likely active sessions: ${status.activeSessionCount.toString()}`,\n ...(sessionLines.length > 0 ? [\"\", \" Sessions:\", ...sessionLines] : []),\n ...(deniedLines.length > 0\n ? [\"\", ` Denied SSH attempts: ${status.deniedAttempts.length.toString()}`, ...deniedLines]\n : []),\n \"\",\n ' Note: Cloud Foundry exposes no live-session API; \"likely active\" sessions',\n \" are inferred from recent ssh-authorized audit events.\",\n ].join(\"\\n\");\n}\n\nexport function formatCrashReport(summary: CrashSummary, now: Date): string {\n if (summary.crashCount === 0) {\n return `No crashes found for ${summary.appName}.`;\n }\n const crashLines = summary.crashes.map((crash) => {\n const index = crash.index === undefined ? \"-\" : `#${crash.index.toString()}`;\n const reason = crash.reason ?? \"unknown\";\n const exit = crash.exitStatus === undefined ? \"\" : ` exit ${crash.exitStatus.toString()}`;\n return ` ${crash.at} instance ${index} ${reason}${exit}`;\n });\n\n return [\n `Crash report for ${summary.appName}`,\n \"\",\n ` Crashes: ${summary.crashCount.toString()}`,\n ...(summary.lastCrashAt === undefined\n ? []\n : [` Last crash: ${summary.lastCrashAt} (${formatRelativeTime(summary.lastCrashAt, now)})`]),\n ...(summary.lastCrashReason === undefined ? [] : [` Last reason: ${summary.lastCrashReason}`]),\n \"\",\n \" Recent crashes:\",\n ...crashLines,\n ].join(\"\\n\");\n}\n\nfunction formatInstanceLine(instance: ProcessInstanceStat): string {\n return [\n ` #${instance.index.toString()}`.padEnd(6),\n instance.state.padEnd(10),\n `up ${formatUptime(instance.uptimeSeconds)}`.padEnd(16),\n `cpu ${formatCpu(instance.cpu)}`.padEnd(13),\n `mem ${formatBytes(instance.memBytes)} / ${formatBytes(instance.memQuotaBytes)}`,\n ].join(\" \");\n}\n\nexport function formatStatusReport(health: AppHealth, now: Date): string {\n const lastEventLine =\n health.lastEvent === undefined\n ? \" Last event: (none)\"\n : ` Last event: ${describeEventType(health.lastEvent.type)} - ${health.lastEvent.createdAt} ` +\n `(${formatRelativeTime(health.lastEvent.createdAt, now)})`;\n\n return [\n `App status: ${health.appName}`,\n \"\",\n ` GUID: ${health.appGuid}`,\n ` Requested state: ${health.requestedState.length > 0 ? health.requestedState : \"unknown\"}`,\n ` SSH enabled: ${health.sshEnabled ? \"yes\" : \"no\"}`,\n ` Instances: ${health.instances.length.toString()}`,\n ...(health.instances.length > 0\n ? [\"\", ...health.instances.map((instance) => formatInstanceLine(instance))]\n : []),\n \"\",\n lastEventLine,\n ].join(\"\\n\");\n}\n","import { setTimeout as delayPromise } from \"node:timers/promises\";\n\nimport { fetchApp, fetchAuditEvents, fetchSshEnabled, fetchWebProcessStats } from \"./api.js\";\nimport type { CurlFn } from \"./api.js\";\nimport { cfAppGuid, cfCurl, prepareCfCliSession, withCfSession } from \"./cf.js\";\nimport { durationToCreatedAfter, summarizeCrashes } from \"./events.js\";\nimport { resolveSelector } from \"./selector.js\";\nimport { buildSshStatus } from \"./ssh.js\";\nimport { CRASH_EVENT_TYPES, SSH_EVENT_TYPES } from \"./types.js\";\nimport type {\n AppHealth,\n AppSummary,\n AuditEvent,\n CfCredentials,\n CrashSummary,\n FetchAuditEventsInput,\n ProcessInstanceStat,\n ResolvedSelector,\n SshEnabled,\n SshStatus,\n} from \"./types.js\";\n\n/** Typed accessor for the Cloud Foundry v3 API used by the runtime. */\nexport interface CfClient {\n readonly fetchAuditEvents: (input: FetchAuditEventsInput) => Promise<readonly AuditEvent[]>;\n readonly fetchApp: (appGuid: string) => Promise<AppSummary>;\n readonly fetchSshEnabled: (appGuid: string) => Promise<SshEnabled>;\n readonly fetchWebProcessStats: (appGuid: string) => Promise<readonly ProcessInstanceStat[]>;\n}\n\nexport interface CfAppSession {\n readonly appGuid: string;\n readonly client: CfClient;\n}\n\n/** Injection seam so the orchestration can be unit-tested without a real CF. */\nexport interface CfEventsDependencies {\n readonly resolveSelector: (raw: string) => Promise<ResolvedSelector>;\n readonly withCfApp: <T>(\n selector: ResolvedSelector,\n credentials: CfCredentials,\n work: (session: CfAppSession) => Promise<T>,\n ) => Promise<T>;\n readonly now: () => Date;\n}\n\nexport interface EventQueryOptions {\n readonly limit: number;\n readonly since: string | undefined;\n readonly types: readonly string[];\n}\n\nexport interface CrashQueryOptions {\n readonly limit: number;\n readonly since: string | undefined;\n}\n\nexport interface WatchOptions {\n readonly intervalMs: number;\n readonly lookback: string;\n readonly types: readonly string[];\n}\n\nconst SSH_STATUS_EVENT_LIMIT = 200;\nconst WATCH_FETCH_LIMIT = 100;\n\nfunction createCfClient(curl: CurlFn): CfClient {\n return {\n fetchAuditEvents: (input) => fetchAuditEvents(input, curl),\n fetchApp: (appGuid) => fetchApp(appGuid, curl),\n fetchSshEnabled: (appGuid) => fetchSshEnabled(appGuid, curl),\n fetchWebProcessStats: (appGuid) => fetchWebProcessStats(appGuid, curl),\n };\n}\n\nasync function defaultWithCfApp<T>(\n selector: ResolvedSelector,\n credentials: CfCredentials,\n work: (session: CfAppSession) => Promise<T>,\n): Promise<T> {\n return await withCfSession(async (ctx) => {\n await prepareCfCliSession(\n {\n apiEndpoint: selector.apiEndpoint,\n orgName: selector.orgName,\n spaceName: selector.spaceName,\n credentials,\n },\n ctx,\n );\n const appGuid = await cfAppGuid(selector.appName, ctx);\n const client = createCfClient((path) => cfCurl(path, ctx));\n return await work({ appGuid, client });\n });\n}\n\nexport function createDefaultDependencies(): CfEventsDependencies {\n return {\n resolveSelector,\n withCfApp: defaultWithCfApp,\n now: () => new Date(),\n };\n}\n\nasync function delay(ms: number, signal: AbortSignal): Promise<void> {\n try {\n await delayPromise(ms, undefined, { signal });\n } catch {\n // The abort signal fired; resolve quietly so the watch loop can re-check it.\n }\n}\n\n/** Orchestrates the cf-events commands on top of an injectable dependency set. */\nexport class CfEventsRuntime {\n private readonly deps: CfEventsDependencies;\n\n constructor(deps: CfEventsDependencies = createDefaultDependencies()) {\n this.deps = deps;\n }\n\n async fetchEvents(\n raw: string,\n credentials: CfCredentials,\n options: EventQueryOptions,\n ): Promise<readonly AuditEvent[]> {\n const selector = await this.deps.resolveSelector(raw);\n return await this.deps.withCfApp(selector, credentials, async ({ appGuid, client }) => {\n return await client.fetchAuditEvents({\n appGuid,\n types: options.types.length > 0 ? options.types : undefined,\n createdAfter: this.resolveCreatedAfter(options.since),\n limit: options.limit,\n });\n });\n }\n\n async getSshStatus(raw: string, credentials: CfCredentials, since: string): Promise<SshStatus> {\n const selector = await this.deps.resolveSelector(raw);\n return await this.deps.withCfApp(selector, credentials, async ({ appGuid, client }) => {\n const [sshEnabled, events] = await Promise.all([\n client.fetchSshEnabled(appGuid),\n client.fetchAuditEvents({\n appGuid,\n types: [...SSH_EVENT_TYPES],\n createdAfter: durationToCreatedAfter(since, this.deps.now()),\n limit: SSH_STATUS_EVENT_LIMIT,\n }),\n ]);\n return buildSshStatus({\n appName: selector.appName,\n sshEnabled: sshEnabled.enabled,\n sshReason: sshEnabled.reason,\n events,\n now: this.deps.now(),\n });\n });\n }\n\n async getCrashes(\n raw: string,\n credentials: CfCredentials,\n options: CrashQueryOptions,\n ): Promise<CrashSummary> {\n const selector = await this.deps.resolveSelector(raw);\n return await this.deps.withCfApp(selector, credentials, async ({ appGuid, client }) => {\n const events = await client.fetchAuditEvents({\n appGuid,\n types: [...CRASH_EVENT_TYPES],\n createdAfter: this.resolveCreatedAfter(options.since),\n limit: options.limit,\n });\n return summarizeCrashes(selector.appName, events);\n });\n }\n\n async getStatus(raw: string, credentials: CfCredentials): Promise<AppHealth> {\n const selector = await this.deps.resolveSelector(raw);\n return await this.deps.withCfApp(selector, credentials, async ({ appGuid, client }) => {\n const [app, instances, sshEnabled, recent] = await Promise.all([\n client.fetchApp(appGuid),\n client.fetchWebProcessStats(appGuid),\n client.fetchSshEnabled(appGuid),\n client.fetchAuditEvents({ appGuid, types: undefined, createdAfter: undefined, limit: 1 }),\n ]);\n return {\n appName: selector.appName,\n appGuid,\n requestedState: app.state,\n sshEnabled: sshEnabled.enabled,\n instances,\n lastEvent: recent[0],\n };\n });\n }\n\n async watchEvents(\n raw: string,\n credentials: CfCredentials,\n options: WatchOptions,\n onEvent: (event: AuditEvent) => void,\n signal: AbortSignal,\n ): Promise<void> {\n const selector = await this.deps.resolveSelector(raw);\n await this.deps.withCfApp(selector, credentials, async ({ appGuid, client }) => {\n const seen = new Set<string>();\n let cursor = durationToCreatedAfter(options.lookback, this.deps.now());\n\n while (!signal.aborted) {\n const events = await client.fetchAuditEvents({\n appGuid,\n types: options.types.length > 0 ? options.types : undefined,\n createdAfter: cursor,\n limit: WATCH_FETCH_LIMIT,\n });\n\n const fresh: AuditEvent[] = [];\n for (const event of events) {\n if (!seen.has(event.guid)) {\n seen.add(event.guid);\n fresh.push(event);\n }\n }\n for (const event of [...fresh].reverse()) {\n onEvent(event);\n }\n\n const newest = events[0];\n if (newest !== undefined && newest.createdAt.length > 0) {\n cursor = newest.createdAt;\n }\n await delay(options.intervalMs, signal);\n }\n });\n }\n\n private resolveCreatedAfter(since: string | undefined): string | undefined {\n return since === undefined ? undefined : durationToCreatedAfter(since, this.deps.now());\n }\n}\n","import type {\n AppSummary,\n AuditEvent,\n CfEntityRef,\n FetchAuditEventsInput,\n ProcessInstanceStat,\n SshEnabled,\n} from \"./types.js\";\n\n/** Calls a Cloud Foundry v3 API path and returns the raw response body. */\nexport type CurlFn = (path: string) => Promise<string>;\n\nconst MAX_PER_PAGE = 100;\n\nfunction parseJson(raw: string): unknown {\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n throw new Error(\"The CF API returned a response that is not valid JSON.\");\n }\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return {};\n}\n\nfunction asArray(value: unknown): readonly unknown[] {\n return Array.isArray(value) ? (value as readonly unknown[]) : [];\n}\n\nfunction asString(value: unknown): string {\n return typeof value === \"string\" ? value : \"\";\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction mapEntityRef(value: unknown): CfEntityRef {\n const record = asRecord(value);\n return {\n guid: asString(record[\"guid\"]),\n type: asString(record[\"type\"]),\n name: asString(record[\"name\"]),\n };\n}\n\nfunction optionalGuid(value: unknown): string | undefined {\n const guid = asString(asRecord(value)[\"guid\"]);\n return guid.length > 0 ? guid : undefined;\n}\n\nfunction mapAuditEvent(value: unknown): AuditEvent {\n const record = asRecord(value);\n return {\n guid: asString(record[\"guid\"]),\n type: asString(record[\"type\"]),\n createdAt: asString(record[\"created_at\"]),\n updatedAt: asString(record[\"updated_at\"]),\n actor: mapEntityRef(record[\"actor\"]),\n target: mapEntityRef(record[\"target\"]),\n data: asRecord(record[\"data\"]),\n spaceGuid: optionalGuid(record[\"space\"]),\n organizationGuid: optionalGuid(record[\"organization\"]),\n };\n}\n\nfunction mapProcessStat(value: unknown): ProcessInstanceStat {\n const record = asRecord(value);\n const usage = asRecord(record[\"usage\"]);\n return {\n type: asString(record[\"type\"]),\n index: asNumber(record[\"index\"]) ?? 0,\n state: asString(record[\"state\"]),\n uptimeSeconds: asNumber(record[\"uptime\"]),\n cpu: asNumber(usage[\"cpu\"]),\n memBytes: asNumber(usage[\"mem\"]),\n memQuotaBytes: asNumber(record[\"mem_quota\"]),\n diskBytes: asNumber(usage[\"disk\"]),\n diskQuotaBytes: asNumber(record[\"disk_quota\"]),\n };\n}\n\nexport function buildAuditEventsPath(input: FetchAuditEventsInput, perPage: number): string {\n const params = new URLSearchParams();\n params.set(\"target_guids\", input.appGuid);\n params.set(\"order_by\", \"-created_at\");\n params.set(\"per_page\", perPage.toString());\n if (input.types !== undefined && input.types.length > 0) {\n params.set(\"types\", input.types.join(\",\"));\n }\n if (input.createdAfter !== undefined) {\n params.set(\"created_ats[gt]\", input.createdAfter);\n }\n return `/v3/audit_events?${params.toString()}`;\n}\n\nfunction nextPagePath(body: unknown): string | undefined {\n const next = asRecord(asRecord(asRecord(body)[\"pagination\"])[\"next\"]);\n const href = next[\"href\"];\n if (typeof href !== \"string\" || href.length === 0) {\n return undefined;\n }\n try {\n const url = new URL(href);\n return `${url.pathname}${url.search}`;\n } catch {\n return undefined;\n }\n}\n\n/** Fetches audit events for an app, following pagination up to `input.limit`. */\nexport async function fetchAuditEvents(\n input: FetchAuditEventsInput,\n curl: CurlFn,\n): Promise<readonly AuditEvent[]> {\n const limit = Math.max(input.limit, 1);\n const perPage = Math.min(limit, MAX_PER_PAGE);\n const events: AuditEvent[] = [];\n let path: string | undefined = buildAuditEventsPath(input, perPage);\n\n while (path !== undefined && events.length < limit) {\n const body = parseJson(await curl(path));\n for (const resource of asArray(asRecord(body)[\"resources\"])) {\n events.push(mapAuditEvent(resource));\n if (events.length >= limit) {\n break;\n }\n }\n path = events.length < limit ? nextPagePath(body) : undefined;\n }\n return events;\n}\n\nexport async function fetchApp(appGuid: string, curl: CurlFn): Promise<AppSummary> {\n const body = asRecord(parseJson(await curl(`/v3/apps/${appGuid}`)));\n return {\n guid: asString(body[\"guid\"]),\n name: asString(body[\"name\"]),\n state: asString(body[\"state\"]),\n };\n}\n\nexport async function fetchSshEnabled(appGuid: string, curl: CurlFn): Promise<SshEnabled> {\n const body = asRecord(parseJson(await curl(`/v3/apps/${appGuid}/ssh_enabled`)));\n return {\n enabled: body[\"enabled\"] === true,\n reason: asString(body[\"reason\"]),\n };\n}\n\nexport async function fetchWebProcessStats(\n appGuid: string,\n curl: CurlFn,\n): Promise<readonly ProcessInstanceStat[]> {\n const body = asRecord(parseJson(await curl(`/v3/apps/${appGuid}/processes/web/stats`)));\n return asArray(body[\"resources\"]).map(mapProcessStat);\n}\n","import { execFile } from \"node:child_process\";\nimport { mkdtemp, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\n\nimport type { CfCliContext, CfSessionInput } from \"./types.js\";\n\nconst execFileAsync = promisify(execFile);\n\nexport interface CurrentCfTarget {\n readonly apiEndpoint: string;\n readonly orgName: string;\n readonly spaceName: string;\n readonly regionKey?: string;\n}\n\nconst REGION_API_MAP: Record<string, string> = {\n ae01: \"https://api.cf.ae01.hana.ondemand.com\",\n ap01: \"https://api.cf.ap01.hana.ondemand.com\",\n ap10: \"https://api.cf.ap10.hana.ondemand.com\",\n ap11: \"https://api.cf.ap11.hana.ondemand.com\",\n ap12: \"https://api.cf.ap12.hana.ondemand.com\",\n ap20: \"https://api.cf.ap20.hana.ondemand.com\",\n ap21: \"https://api.cf.ap21.hana.ondemand.com\",\n ap30: \"https://api.cf.ap30.hana.ondemand.com\",\n br10: \"https://api.cf.br10.hana.ondemand.com\",\n br20: \"https://api.cf.br20.hana.ondemand.com\",\n br30: \"https://api.cf.br30.hana.ondemand.com\",\n ca10: \"https://api.cf.ca10.hana.ondemand.com\",\n ca20: \"https://api.cf.ca20.hana.ondemand.com\",\n ch20: \"https://api.cf.ch20.hana.ondemand.com\",\n cn20: \"https://api.cf.cn20.platform.sapcloud.cn\",\n cn40: \"https://api.cf.cn40.platform.sapcloud.cn\",\n eu01: \"https://api.cf.eu01.hana.ondemand.com\",\n eu02: \"https://api.cf.eu02.hana.ondemand.com\",\n eu10: \"https://api.cf.eu10.hana.ondemand.com\",\n \"eu10-002\": \"https://api.cf.eu10-002.hana.ondemand.com\",\n \"eu10-003\": \"https://api.cf.eu10-003.hana.ondemand.com\",\n \"eu10-004\": \"https://api.cf.eu10-004.hana.ondemand.com\",\n \"eu10-005\": \"https://api.cf.eu10-005.hana.ondemand.com\",\n \"eu10-006\": \"https://api.cf.eu10-006.hana.ondemand.com\",\n eu11: \"https://api.cf.eu11.hana.ondemand.com\",\n eu13: \"https://api.cf.eu13.hana.ondemand.com\",\n eu20: \"https://api.cf.eu20.hana.ondemand.com\",\n \"eu20-001\": \"https://api.cf.eu20-001.hana.ondemand.com\",\n eu30: \"https://api.cf.eu30.hana.ondemand.com\",\n in30: \"https://api.cf.in30.hana.ondemand.com\",\n jp10: \"https://api.cf.jp10.hana.ondemand.com\",\n jp20: \"https://api.cf.jp20.hana.ondemand.com\",\n jp30: \"https://api.cf.jp30.hana.ondemand.com\",\n jp31: \"https://api.cf.jp31.hana.ondemand.com\",\n sa30: \"https://api.cf.sa30.hana.ondemand.com\",\n us10: \"https://api.cf.us10.hana.ondemand.com\",\n \"us10-001\": \"https://api.cf.us10-001.hana.ondemand.com\",\n \"us10-002\": \"https://api.cf.us10-002.hana.ondemand.com\",\n us20: \"https://api.cf.us20.hana.ondemand.com\",\n us21: \"https://api.cf.us21.hana.ondemand.com\",\n us30: \"https://api.cf.us30.hana.ondemand.com\",\n // add more if needed, this covers common SAP BTP regions\n};\n\nexport function getApiEndpointForRegion(regionKey: string): string | undefined {\n return REGION_API_MAP[regionKey];\n}\n\nexport function getRegionKeyForApi(apiEndpoint: string): string | undefined {\n const normalized = apiEndpoint.trim().replace(/\\/+$/, \"\").toLowerCase();\n for (const [key, api] of Object.entries(REGION_API_MAP)) {\n if (api.toLowerCase() === normalized) {\n return key;\n }\n }\n return undefined;\n}\n\nexport async function readCurrentCfTarget(): Promise<CurrentCfTarget | undefined> {\n const cfBin = process.env[\"CF_EVENTS_CF_BIN\"] ?? \"cf\";\n const isScript = cfBin.endsWith(\".mjs\") || cfBin.endsWith(\".js\");\n const file = isScript ? \"node\" : cfBin;\n const args = isScript ? [cfBin, \"target\"] : [\"target\"];\n try {\n const { stdout } = await execFileAsync(file, args, {\n maxBuffer: 1024 * 1024,\n timeout: 10000,\n });\n return parseCfTargetOutput(stdout);\n } catch {\n return undefined;\n }\n}\n\nexport function parseCfTargetOutput(stdout: string): CurrentCfTarget | undefined {\n const fields = parseCfTargetFields(stdout);\n const apiEndpoint = fields.get(\"api endpoint\");\n const orgName = fields.get(\"org\");\n const spaceName = fields.get(\"space\");\n if (!apiEndpoint || !orgName || !spaceName) {\n return undefined;\n }\n const regionKey = getRegionKeyForApi(apiEndpoint);\n return {\n apiEndpoint,\n orgName,\n spaceName,\n ...(regionKey ? { regionKey } : {}),\n };\n}\n\nfunction parseCfTargetFields(stdout: string): Map<string, string> {\n const fields = new Map<string, string>();\n for (const line of stdout.split(/\\r?\\n/)) {\n const sep = line.indexOf(\":\");\n if (sep < 0) {\n continue;\n }\n const key = line.slice(0, sep).trim().toLowerCase();\n const value = line.slice(sep + 1).trim();\n if (key && value) {\n fields.set(key, value);\n }\n }\n return fields;\n}\n\nconst CF_MAX_BUFFER_BYTES = 16 * 1024 * 1024;\nconst CF_COMMAND_TIMEOUT_MS = 30_000;\nconst CF_RETRY_MAX_ATTEMPTS = 3;\nconst CF_RETRY_BASE_DELAY_MS = 120;\nconst GUID_PATTERN = /^[0-9a-f-]{16,}$/i;\n\ninterface ResolvedCommand {\n readonly bin: string;\n readonly argsPrefix: readonly string[];\n}\n\n/**\n * Runs `work` against an isolated, ephemeral `CF_HOME` so concurrent CF\n * sessions never share login state, and the directory is always cleaned up.\n */\nexport async function withCfSession<T>(work: (ctx: CfCliContext) => Promise<T>): Promise<T> {\n const cfHomeDir = await mkdtemp(join(tmpdir(), \"saptools-cf-events-\"));\n try {\n return await work({ cfHomeDir });\n } finally {\n await rm(cfHomeDir, { recursive: true, force: true });\n }\n}\n\n/** Authenticates the CF CLI and targets the requested org/space. */\nexport async function prepareCfCliSession(input: CfSessionInput, ctx: CfCliContext): Promise<void> {\n await runCfCommand([\"api\", input.apiEndpoint], ctx, \"Failed to set the CF API endpoint.\");\n await runCfCommand([\"auth\"], ctx, \"Failed to authenticate the Cloud Foundry CLI.\", {\n CF_USERNAME: input.credentials.email,\n CF_PASSWORD: input.credentials.password,\n });\n await runCfCommand(\n [\"target\", \"-o\", input.orgName, \"-s\", input.spaceName],\n ctx,\n \"Failed to target the CF org and space.\",\n );\n}\n\n/** Resolves the GUID of a targeted app via `cf app <name> --guid`. */\nexport async function cfAppGuid(appName: string, ctx: CfCliContext): Promise<string> {\n const stdout = await runCfCommand(\n [\"app\", appName, \"--guid\"],\n ctx,\n `Failed to resolve the GUID for app \"${appName}\".`,\n );\n const guid = stdout.trim();\n if (!GUID_PATTERN.test(guid)) {\n throw new Error(\n `Could not resolve the GUID for app \"${appName}\" - the CF CLI returned an unexpected value.`,\n );\n }\n return guid;\n}\n\n/** Calls a Cloud Foundry v3 API path via `cf curl` and returns the raw body. */\nexport async function cfCurl(path: string, ctx: CfCliContext): Promise<string> {\n return await runCfCommand([\"curl\", path], ctx, `Failed to call the CF API path \"${path}\".`);\n}\n\nexport async function runCfCommand(\n args: readonly string[],\n ctx: CfCliContext,\n failureMessage: string,\n envOverrides: Readonly<Record<string, string>> = {},\n): Promise<string> {\n const command = resolveCommand();\n const env = buildEnv(ctx.cfHomeDir, envOverrides);\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= CF_RETRY_MAX_ATTEMPTS; attempt += 1) {\n try {\n return await execFileWithResult(command.bin, [...command.argsPrefix, ...args], env);\n } catch (error) {\n lastError = error;\n if (attempt >= CF_RETRY_MAX_ATTEMPTS || !shouldRetryCfCliError(error)) {\n break;\n }\n await sleep(resolveRetryDelayMs(attempt));\n }\n }\n\n const detail = extractSafeCliDetail(lastError);\n throw new Error(detail.length > 0 ? `${failureMessage} ${detail}` : failureMessage, {\n cause: lastError,\n });\n}\n\nfunction resolveCommand(): ResolvedCommand {\n const bin = process.env[\"CF_EVENTS_CF_BIN\"] ?? \"cf\";\n if (/\\.(?:c|m)?js$/i.test(bin)) {\n return { bin: process.execPath, argsPrefix: [bin] };\n }\n return { bin, argsPrefix: [] };\n}\n\nfunction buildEnv(cfHomeDir: string, overrides: Readonly<Record<string, string>>): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = { ...process.env };\n delete env[\"SAP_EMAIL\"];\n delete env[\"SAP_PASSWORD\"];\n env[\"CF_HOME\"] = cfHomeDir;\n Object.assign(env, overrides);\n return env;\n}\n\nasync function execFileWithResult(\n command: string,\n args: readonly string[],\n env: NodeJS.ProcessEnv,\n): Promise<string> {\n return await new Promise<string>((resolvePromise, rejectPromise) => {\n execFile(\n command,\n [...args],\n { env, maxBuffer: CF_MAX_BUFFER_BYTES, timeout: CF_COMMAND_TIMEOUT_MS },\n (error: Error | null, stdout: string, stderr: string) => {\n if (error !== null) {\n rejectPromise(Object.assign(error, { stdout, stderr }));\n return;\n }\n resolvePromise(stdout);\n },\n );\n });\n}\n\nfunction shouldRetryCfCliError(error: unknown): boolean {\n const haystack = `${readErrorStderr(error)} ${readErrorMessage(error)}`.toLowerCase();\n if (\n haystack.includes(\"invalid\") ||\n haystack.includes(\"credentials were rejected\") ||\n haystack.includes(\"authentication failed\") ||\n haystack.includes(\"not authorized\") ||\n haystack.includes(\"forbidden\")\n ) {\n return false;\n }\n return (\n haystack.includes(\"timeout\") ||\n haystack.includes(\"timed out\") ||\n haystack.includes(\"connection reset\") ||\n haystack.includes(\"temporarily unavailable\") ||\n haystack.includes(\"network\") ||\n haystack.includes(\"econnreset\") ||\n haystack.includes(\"econnrefused\")\n );\n}\n\nfunction readErrorStderr(error: unknown): string {\n if (typeof error !== \"object\" || error === null) {\n return \"\";\n }\n const stderr = (error as { readonly stderr?: unknown }).stderr;\n return typeof stderr === \"string\" ? stderr : \"\";\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : \"\";\n}\n\nfunction resolveRetryDelayMs(attempt: number): number {\n const jitter = Math.floor(Math.random() * CF_RETRY_BASE_DELAY_MS);\n return CF_RETRY_BASE_DELAY_MS * attempt + jitter;\n}\n\nasync function sleep(delayMs: number): Promise<void> {\n await new Promise<void>((resolvePromise) => {\n setTimeout(resolvePromise, delayMs);\n });\n}\n\nfunction extractSafeCliDetail(error: unknown): string {\n const stderr = readErrorStderr(error);\n if (stderr.length === 0) {\n return \"\";\n }\n const cleaned = stderr.replaceAll(/\\s+/g, \" \").trim();\n return cleaned.length > 0 ? `(cli: ${cleaned.slice(0, 180)})` : \"\";\n}\n","import { getApiEndpointForRegion, readCurrentCfTarget } from \"./cf.js\";\nimport type { ResolvedSelector } from \"./types.js\";\n\nexport interface ParsedAppName {\n readonly kind: \"appName\";\n readonly appName: string;\n}\n\nexport interface ParsedExplicit {\n readonly kind: \"explicit\";\n readonly regionKey: string;\n readonly orgName: string;\n readonly spaceName: string;\n readonly appName: string;\n}\n\nexport type ParsedSelector = ParsedAppName | ParsedExplicit;\n\nconst SELECTOR_USAGE = 'use \"region/org/space/app\" or a bare app name';\n\n/** Parses a raw selector string into its structural parts without any I/O. */\nexport function parseSelector(raw: string): ParsedSelector {\n const trimmed = raw.trim();\n if (trimmed.length === 0) {\n throw new Error(`A selector is required: ${SELECTOR_USAGE}.`);\n }\n\n const parts = trimmed.split(\"/\").map((part) => part.trim());\n\n if (parts.length === 1) {\n const appName = parts[0] ?? \"\";\n if (appName.length === 0) {\n throw new Error(`A selector is required: ${SELECTOR_USAGE}.`);\n }\n return { kind: \"appName\", appName };\n }\n\n if (parts.length === 4) {\n const [regionKey, orgName, spaceName, appName] = parts;\n if (\n regionKey === undefined ||\n orgName === undefined ||\n spaceName === undefined ||\n appName === undefined ||\n regionKey.length === 0 ||\n orgName.length === 0 ||\n spaceName.length === 0 ||\n appName.length === 0\n ) {\n throw new Error(`Invalid selector \"${raw}\": every region/org/space/app segment must be non-empty.`);\n }\n return { kind: \"explicit\", regionKey, orgName, spaceName, appName };\n }\n\n throw new Error(`Invalid selector \"${raw}\": ${SELECTOR_USAGE}.`);\n}\n\n/**\n * Resolves a raw selector.\n * - Bare app name: based strictly on the CURRENT CF target (no global search).\n * - Full path: uses known region-to-api mapping; trusts the provided org/space/app (no snapshot validation).\n */\nexport async function resolveSelector(raw: string): Promise<ResolvedSelector> {\n const parsed = parseSelector(raw);\n\n if (parsed.kind === \"appName\") {\n const current = await readCurrentCfTarget();\n if (!current) {\n throw new Error(\n \"No current CF target found. Run `cf target -o <org> -s <space>` or pass a full region/org/space/app selector.\",\n );\n }\n return {\n raw,\n regionKey: current.regionKey ?? \"\",\n apiEndpoint: current.apiEndpoint,\n orgName: current.orgName,\n spaceName: current.spaceName,\n appName: parsed.appName,\n };\n }\n\n // explicit full path\n const apiEndpoint = getApiEndpointForRegion(parsed.regionKey);\n if (!apiEndpoint) {\n throw new Error(\n `Unknown region \"${parsed.regionKey}\". Use a bare app name (requires current CF target) or a known region key.`,\n );\n }\n\n return {\n raw,\n regionKey: parsed.regionKey,\n apiEndpoint,\n orgName: parsed.orgName,\n spaceName: parsed.spaceName,\n appName: parsed.appName,\n };\n}\n","import { isSshEvent } from \"./events.js\";\nimport type { AuditEvent, SshSession, SshStatus } from \"./types.js\";\n\nconst SSH_AUTHORIZED_TYPE = \"audit.app.ssh-authorized\";\nconst SSH_DENIED_TYPE = \"audit.app.ssh-unauthorized\";\n\n/**\n * Window after an `ssh-authorized` event during which the session is treated\n * as \"likely active\". Cloud Foundry emits no session-close event, so this is\n * a heuristic rather than a live-session check.\n */\nconst LIKELY_ACTIVE_WINDOW_MS = 60 * 60 * 1000;\n\nfunction isWithinActiveWindow(timestamp: string, now: Date): boolean {\n const at = Date.parse(timestamp);\n if (Number.isNaN(at)) {\n return false;\n }\n const delta = now.getTime() - at;\n return delta >= 0 && delta <= LIKELY_ACTIVE_WINDOW_MS;\n}\n\n/** Infers SSH/debug sessions from `ssh-authorized` audit events. */\nexport function inferSshSessions(events: readonly AuditEvent[], now: Date): readonly SshSession[] {\n return events\n .filter((event) => event.type === SSH_AUTHORIZED_TYPE)\n .map((event) => ({\n actor: event.actor.name.length > 0 ? event.actor.name : event.actor.guid,\n authorizedAt: event.createdAt,\n likelyActive: isWithinActiveWindow(event.createdAt, now),\n }))\n .sort((left, right) => right.authorizedAt.localeCompare(left.authorizedAt));\n}\n\nexport interface SshStatusInput {\n readonly appName: string;\n readonly sshEnabled: boolean;\n readonly sshReason: string;\n readonly events: readonly AuditEvent[];\n readonly now: Date;\n}\n\nexport function buildSshStatus(input: SshStatusInput): SshStatus {\n const sshEvents = input.events.filter(isSshEvent);\n const sessions = inferSshSessions(sshEvents, input.now);\n const deniedAttempts = sshEvents\n .filter((event) => event.type === SSH_DENIED_TYPE)\n .sort((left, right) => right.createdAt.localeCompare(left.createdAt));\n\n return {\n appName: input.appName,\n sshEnabled: input.sshEnabled,\n sshReason: input.sshReason,\n sessions,\n deniedAttempts,\n activeSessionCount: sessions.filter((session) => session.likelyActive).length,\n };\n}\n"],"mappings":"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","names":["join","process","process","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/types.ts","../src/events.ts","../src/format.ts","../src/runtime.ts","../src/api.ts","../src/cf.ts","../src/selector.ts","../src/ssh.ts"],"sourcesContent":["import { readFileSync, realpathSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport process from \"node:process\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { Command } from \"commander\";\n\nimport { parseTypeFilter } from \"./events.js\";\nimport {\n formatCrashReport,\n formatEventLine,\n formatEventsReport,\n formatSpaceEventLine,\n formatSpaceEventsReport,\n formatSshStatusReport,\n formatStatusReport,\n} from \"./format.js\";\nimport { CfEventsRuntime } from \"./runtime.js\";\nimport { parseSelector } from \"./selector.js\";\nimport type { AuditEvent, CfCredentials } from \"./types.js\";\n\nconst DEFAULT_EVENT_LIMIT = 50;\nconst DEFAULT_CRASH_LIMIT = 50;\nconst DEFAULT_SSH_SINCE = \"24h\";\nconst DEFAULT_WATCH_INTERVAL_MS = 15_000;\nconst DEFAULT_WATCH_LOOKBACK = \"2m\";\nconst MIN_WATCH_INTERVAL_MS = 2000;\n\ninterface CommonFlags {\n readonly email?: string;\n readonly password?: string;\n readonly json?: boolean;\n}\n\ninterface EventsFlags extends CommonFlags {\n readonly limit?: number;\n readonly since?: string;\n readonly type?: string;\n}\n\ninterface SshStatusFlags extends CommonFlags {\n readonly since?: string;\n}\n\ninterface CrashesFlags extends CommonFlags {\n readonly limit?: number;\n readonly since?: string;\n}\n\ninterface WatchFlags extends CommonFlags {\n readonly interval?: number;\n readonly lookback?: string;\n readonly type?: string;\n}\n\nfunction resolveCredential(value: string | undefined, envName: string, optionName: string): string {\n const direct = value?.trim();\n if (direct !== undefined && direct.length > 0) {\n return direct;\n }\n const fromEnv = process.env[envName]?.trim();\n if (fromEnv !== undefined && fromEnv.length > 0) {\n return fromEnv;\n }\n throw new Error(`Missing SAP credentials. Pass --${optionName} or set the ${envName} environment variable.`);\n}\n\nfunction buildCredentials(flags: CommonFlags): CfCredentials {\n return {\n email: resolveCredential(flags.email, \"SAP_EMAIL\", \"email\"),\n password: resolveCredential(flags.password, \"SAP_PASSWORD\", \"password\"),\n };\n}\n\nfunction parsePositiveInteger(value: string, optionName: string): number {\n const parsed = Number.parseInt(value, 10);\n if (!Number.isInteger(parsed) || parsed <= 0) {\n throw new Error(`${optionName} must be a positive integer.`);\n }\n return parsed;\n}\n\nfunction appLabel(selector: string): string {\n return (selector.split(\"/\").at(-1) ?? selector).trim();\n}\n\nfunction isSpaceSelector(selector: string): boolean {\n return parseSelector(selector).kind === \"spacePath\";\n}\n\nfunction spaceLabel(selector: string): string {\n const parsed = parseSelector(selector);\n return parsed.kind === \"spacePath\" ? `${parsed.regionKey}/${parsed.orgName}/${parsed.spaceName}` : selector;\n}\n\nfunction resolveSelectorArgument(selector: string): string {\n // Bare app name or full path is passed as-is.\n // Bare names are resolved inside resolveSelector using the current CF target (no global snapshot search).\n return selector;\n}\n\nfunction writeOut(text: string): void {\n process.stdout.write(text.endsWith(\"\\n\") ? text : `${text}\\n`);\n}\n\nfunction writeJson(value: unknown): void {\n process.stdout.write(`${JSON.stringify(value, null, 2)}\\n`);\n}\n\nfunction writeJsonLine(value: unknown): void {\n process.stdout.write(`${JSON.stringify(value)}\\n`);\n}\n\nasync function runEvents(selector: string, flags: EventsFlags): Promise<void> {\n const resolvedSelector = resolveSelectorArgument(selector);\n const runtime = new CfEventsRuntime();\n const events = await runtime.fetchEvents(resolvedSelector, buildCredentials(flags), {\n limit: flags.limit ?? DEFAULT_EVENT_LIMIT,\n since: flags.since,\n types: parseTypeFilter(flags.type),\n });\n if (flags.json === true) {\n writeJson(events);\n return;\n }\n writeOut(isSpaceSelector(resolvedSelector)\n ? formatSpaceEventsReport(spaceLabel(resolvedSelector), events, new Date())\n : formatEventsReport(appLabel(resolvedSelector), events, new Date()));\n}\n\nasync function runSshStatus(selector: string, flags: SshStatusFlags): Promise<void> {\n const resolvedSelector = resolveSelectorArgument(selector);\n const runtime = new CfEventsRuntime();\n const status = await runtime.getSshStatus(\n resolvedSelector,\n buildCredentials(flags),\n flags.since ?? DEFAULT_SSH_SINCE,\n );\n if (flags.json === true) {\n writeJson(status);\n return;\n }\n writeOut(formatSshStatusReport(status, new Date()));\n}\n\nasync function runCrashes(selector: string, flags: CrashesFlags): Promise<void> {\n const resolvedSelector = resolveSelectorArgument(selector);\n const runtime = new CfEventsRuntime();\n const summary = await runtime.getCrashes(resolvedSelector, buildCredentials(flags), {\n limit: flags.limit ?? DEFAULT_CRASH_LIMIT,\n since: flags.since,\n });\n if (flags.json === true) {\n writeJson(summary);\n return;\n }\n writeOut(formatCrashReport(summary, new Date()));\n}\n\nasync function runStatus(selector: string, flags: CommonFlags): Promise<void> {\n const resolvedSelector = resolveSelectorArgument(selector);\n const runtime = new CfEventsRuntime();\n const health = await runtime.getStatus(resolvedSelector, buildCredentials(flags));\n if (flags.json === true) {\n writeJson(health);\n return;\n }\n writeOut(formatStatusReport(health, new Date()));\n}\n\nfunction emitWatchEvent(event: AuditEvent, asJson: boolean, spaceScope: boolean): void {\n if (asJson) {\n writeJsonLine(event);\n return;\n }\n writeOut(spaceScope ? formatSpaceEventLine(event, new Date()) : formatEventLine(event, new Date()));\n}\n\nasync function runWatch(selector: string, flags: WatchFlags): Promise<void> {\n const resolvedSelector = resolveSelectorArgument(selector);\n const interval = flags.interval ?? DEFAULT_WATCH_INTERVAL_MS;\n if (interval < MIN_WATCH_INTERVAL_MS) {\n throw new Error(`--interval must be at least ${MIN_WATCH_INTERVAL_MS.toString()}ms.`);\n }\n\n const runtime = new CfEventsRuntime();\n const controller = new AbortController();\n const cleanup = bindTerminationSignals(() => {\n controller.abort();\n });\n const asJson = flags.json === true;\n const spaceScope = isSpaceSelector(resolvedSelector);\n\n try {\n process.stderr.write(`Watching ${resolvedSelector} for new audit events. Press Ctrl+C to stop.\\n`);\n await runtime.watchEvents(\n resolvedSelector,\n buildCredentials(flags),\n {\n intervalMs: interval,\n lookback: flags.lookback ?? DEFAULT_WATCH_LOOKBACK,\n types: parseTypeFilter(flags.type),\n },\n (event) => {\n emitWatchEvent(event, asJson, spaceScope);\n },\n controller.signal,\n );\n } finally {\n cleanup();\n }\n}\n\nfunction bindTerminationSignals(stop: () => void): () => void {\n const handler = (): void => {\n stop();\n };\n process.once(\"SIGINT\", handler);\n process.once(\"SIGTERM\", handler);\n return () => {\n process.off(\"SIGINT\", handler);\n process.off(\"SIGTERM\", handler);\n };\n}\n\nfunction addCommonOptions(command: Command): Command {\n return command\n .option(\"--email <value>\", \"SAP email (default: SAP_EMAIL env var)\")\n .option(\"--password <value>\", \"SAP password (default: SAP_PASSWORD env var)\")\n .option(\"--json\", \"Emit structured JSON instead of text\", false);\n}\n\nfunction readPackageVersion(): string {\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n const raw = readFileSync(join(here, \"..\", \"package.json\"), \"utf8\");\n const parsed = JSON.parse(raw) as { readonly version?: unknown };\n return typeof parsed.version === \"string\" && parsed.version.length > 0 ? parsed.version : \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\nfunction suppressBrokenPipe(): void {\n for (const stream of [process.stdout, process.stderr]) {\n stream.on(\"error\", (error: NodeJS.ErrnoException): void => {\n if (error.code === \"EPIPE\") {\n process.exit(0);\n }\n throw error;\n });\n }\n}\n\nfunction buildProgram(): Command {\n const program = new Command();\n program\n .name(\"cf-events\")\n .description(\"Inspect SAP BTP Cloud Foundry application audit events and SSH/debug sessions\")\n .version(readPackageVersion(), \"-V, --version\", \"Print the cf-events package version\");\n\n addCommonOptions(\n program\n .command(\"events\")\n .description(\"List recent audit events for an app or all apps in a space\")\n .argument(\"<selector>\", \"region/org/space, region/org/space/app, or a bare app name\"),\n )\n .option(\n \"--limit <count>\",\n \"Maximum number of events to return\",\n (value: string) => parsePositiveInteger(value, \"--limit\"),\n )\n .option(\"--since <duration>\", \"Only events newer than a duration (e.g. 30m, 6h, 7d)\")\n .option(\"--type <types>\", 'Comma-separated CF event types, or the shorthand \"ssh\"/\"crash\"')\n .action(async (selector: string, flags: EventsFlags): Promise<void> => {\n await runEvents(selector, flags);\n });\n\n addCommonOptions(\n program\n .command(\"ssh-status\")\n .description(\"Show whether SSH is enabled and detect recent SSH/debug sessions\")\n .argument(\"<selector>\", \"region/org/space/app or a bare app name\"),\n )\n .option(\"--since <duration>\", \"Look-back window for SSH events\", DEFAULT_SSH_SINCE)\n .action(async (selector: string, flags: SshStatusFlags): Promise<void> => {\n await runSshStatus(selector, flags);\n });\n\n addCommonOptions(\n program\n .command(\"crashes\")\n .description(\"Summarize recent crash events for an app or all apps in a space\")\n .argument(\"<selector>\", \"region/org/space, region/org/space/app, or a bare app name\"),\n )\n .option(\n \"--limit <count>\",\n \"Maximum number of crash events to inspect\",\n (value: string) => parsePositiveInteger(value, \"--limit\"),\n )\n .option(\"--since <duration>\", \"Only crashes newer than a duration (e.g. 30m, 6h, 7d)\")\n .action(async (selector: string, flags: CrashesFlags): Promise<void> => {\n await runCrashes(selector, flags);\n });\n\n addCommonOptions(\n program\n .command(\"status\")\n .description(\"Show app health: requested state, instances, SSH flag, and last event\")\n .argument(\"<selector>\", \"region/org/space/app or a bare app name\"),\n ).action(async (selector: string, flags: CommonFlags): Promise<void> => {\n await runStatus(selector, flags);\n });\n\n addCommonOptions(\n program\n .command(\"watch\")\n .description(\"Poll for new audit events for an app or all apps in a space\")\n .argument(\"<selector>\", \"region/org/space, region/org/space/app, or a bare app name\"),\n )\n .option(\n \"--interval <ms>\",\n \"Polling interval in milliseconds\",\n (value: string) => parsePositiveInteger(value, \"--interval\"),\n )\n .option(\"--lookback <duration>\", \"Initial look-back window on start (e.g. 2m)\", DEFAULT_WATCH_LOOKBACK)\n .option(\"--type <types>\", 'Comma-separated CF event types, or the shorthand \"ssh\"/\"crash\"')\n .action(async (selector: string, flags: WatchFlags): Promise<void> => {\n await runWatch(selector, flags);\n });\n\n return program;\n}\n\nexport async function main(argv: readonly string[]): Promise<void> {\n const program = buildProgram();\n await program.parseAsync([...argv]);\n}\n\nfunction isMainModule(): boolean {\n const executedPath = process.argv[1];\n if (executedPath === undefined) {\n return false;\n }\n try {\n return realpathSync(executedPath) === fileURLToPath(import.meta.url);\n } catch {\n return resolve(executedPath) === fileURLToPath(import.meta.url);\n }\n}\n\nasync function runCli(): Promise<void> {\n suppressBrokenPipe();\n try {\n await main(process.argv);\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`Error: ${message}\\n`);\n process.exit(1);\n }\n}\n\nif (isMainModule()) {\n await runCli();\n}\n","export type RegionKey = string;\n\n/** Cloud Foundry v3 audit event types that target an application. */\nexport const APP_EVENT_TYPES = [\n \"audit.app.create\",\n \"audit.app.update\",\n \"audit.app.delete-request\",\n \"audit.app.start\",\n \"audit.app.stop\",\n \"audit.app.restage\",\n \"audit.app.ssh-authorized\",\n \"audit.app.ssh-unauthorized\",\n \"audit.app.crash\",\n \"audit.app.process.create\",\n \"audit.app.process.scale\",\n \"audit.app.process.update\",\n \"audit.app.process.crash\",\n \"audit.app.process.terminate_instance\",\n \"audit.app.map-route\",\n \"audit.app.unmap-route\",\n \"audit.app.environment_variables.show\",\n] as const;\n\nexport type AppEventType = (typeof APP_EVENT_TYPES)[number];\n\n/** Event types emitted when an SSH/debug connection is authorized or denied. */\nexport const SSH_EVENT_TYPES = [\"audit.app.ssh-authorized\", \"audit.app.ssh-unauthorized\"] as const;\n\n/** Event types emitted when an application instance crashes. */\nexport const CRASH_EVENT_TYPES = [\"audit.app.crash\", \"audit.app.process.crash\"] as const;\n\nexport interface CfCredentials {\n readonly email: string;\n readonly password: string;\n}\n\n/** A fully resolved application target. */\nexport interface ResolvedAppSelector {\n readonly kind: \"app\";\n readonly raw: string;\n readonly regionKey: RegionKey;\n readonly apiEndpoint: string;\n readonly orgName: string;\n readonly spaceName: string;\n readonly appName: string;\n}\n\n/** A fully resolved space target for space-wide audit-event queries. */\nexport interface ResolvedSpaceSelector {\n readonly kind: \"space\";\n readonly raw: string;\n readonly regionKey: RegionKey;\n readonly apiEndpoint: string;\n readonly orgName: string;\n readonly spaceName: string;\n}\n\nexport type ResolvedSelector = ResolvedAppSelector | ResolvedSpaceSelector;\n\nexport interface CfSessionInput {\n readonly apiEndpoint: string;\n readonly orgName: string;\n readonly spaceName: string;\n readonly credentials: CfCredentials;\n}\n\n/** Handle for a CF CLI session bound to an isolated, ephemeral `CF_HOME`. */\nexport interface CfCliContext {\n readonly cfHomeDir: string;\n}\n\nexport interface CfEntityRef {\n readonly guid: string;\n readonly type: string;\n readonly name: string;\n}\n\nexport interface AuditEvent {\n readonly guid: string;\n readonly type: string;\n readonly createdAt: string;\n readonly updatedAt: string;\n readonly actor: CfEntityRef;\n readonly target: CfEntityRef;\n readonly data: Record<string, unknown>;\n readonly spaceGuid: string | undefined;\n readonly organizationGuid: string | undefined;\n}\n\nexport interface AppSummary {\n readonly guid: string;\n readonly name: string;\n readonly state: string;\n}\n\nexport interface SshEnabled {\n readonly enabled: boolean;\n readonly reason: string;\n}\n\nexport interface ProcessInstanceStat {\n readonly type: string;\n readonly index: number;\n readonly state: string;\n readonly uptimeSeconds: number | undefined;\n readonly cpu: number | undefined;\n readonly memBytes: number | undefined;\n readonly memQuotaBytes: number | undefined;\n readonly diskBytes: number | undefined;\n readonly diskQuotaBytes: number | undefined;\n}\n\nexport interface CrashRecord {\n readonly at: string;\n readonly index: number | undefined;\n readonly reason: string | undefined;\n readonly exitStatus: number | undefined;\n}\n\nexport interface CrashSummary {\n readonly appName: string;\n readonly crashCount: number;\n readonly lastCrashAt: string | undefined;\n readonly lastCrashReason: string | undefined;\n readonly crashes: readonly CrashRecord[];\n}\n\n/**\n * An SSH/debug session inferred from a single `ssh-authorized` audit event.\n * Cloud Foundry does not expose live sessions, so `likelyActive` is a\n * best-effort signal based on how recently the session was authorized.\n */\nexport interface SshSession {\n readonly actor: string;\n readonly authorizedAt: string;\n readonly likelyActive: boolean;\n}\n\nexport interface SshStatus {\n readonly appName: string;\n readonly sshEnabled: boolean;\n readonly sshReason: string;\n readonly sessions: readonly SshSession[];\n readonly deniedAttempts: readonly AuditEvent[];\n readonly activeSessionCount: number;\n}\n\nexport interface AppHealth {\n readonly appName: string;\n readonly appGuid: string;\n readonly requestedState: string;\n readonly sshEnabled: boolean;\n readonly instances: readonly ProcessInstanceStat[];\n readonly lastEvent: AuditEvent | undefined;\n}\n\nexport interface AppCrashSummary {\n readonly appName: string;\n readonly crashCount: number;\n readonly lastCrashAt: string | undefined;\n readonly lastCrashReason: string | undefined;\n readonly crashes: readonly CrashRecord[];\n}\n\nexport interface SpaceCrashSummary {\n readonly scope: \"space\";\n readonly selector: string;\n readonly crashCount: number;\n readonly lastCrashAt: string | undefined;\n readonly apps: readonly AppCrashSummary[];\n}\n\nexport type CrashReportSummary = CrashSummary | SpaceCrashSummary;\n\nexport type AuditEventScope =\n | { readonly kind: \"app\"; readonly appGuid: string }\n | { readonly kind: \"space\"; readonly spaceGuid: string };\n\nexport interface FetchAuditEventsInput {\n readonly scope: AuditEventScope;\n readonly types: readonly string[] | undefined;\n readonly createdAfter: string | undefined;\n readonly limit: number;\n}\n","import type { AppCrashSummary, AuditEvent, CrashRecord, CrashSummary, SpaceCrashSummary } from \"./types.js\";\nimport { CRASH_EVENT_TYPES, SSH_EVENT_TYPES } from \"./types.js\";\n\nconst DURATION_PATTERN = /^(\\d+)\\s*(s|m|h|d)$/;\n\nconst DURATION_UNIT_MS: Readonly<Record<string, number>> = {\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n};\n\nexport function isCrashEvent(event: AuditEvent): boolean {\n return (CRASH_EVENT_TYPES as readonly string[]).includes(event.type);\n}\n\nexport function isSshEvent(event: AuditEvent): boolean {\n return (SSH_EVENT_TYPES as readonly string[]).includes(event.type);\n}\n\nexport function filterByTypes(\n events: readonly AuditEvent[],\n types: readonly string[],\n): readonly AuditEvent[] {\n if (types.length === 0) {\n return events;\n }\n const allowed = new Set(types);\n return events.filter((event) => allowed.has(event.type));\n}\n\nexport function sortEventsNewestFirst(events: readonly AuditEvent[]): readonly AuditEvent[] {\n return [...events].sort((left, right) => right.createdAt.localeCompare(left.createdAt));\n}\n\n/** Parses a human duration such as `30m`, `6h`, or `7d` into milliseconds. */\nexport function parseDuration(raw: string): number {\n const match = DURATION_PATTERN.exec(raw.trim().toLowerCase());\n const amountToken = match?.[1];\n const unitToken = match?.[2];\n if (amountToken === undefined || unitToken === undefined) {\n throw new Error(`Invalid duration \"${raw}\". Use forms like 30m, 6h, or 7d.`);\n }\n\n const amount = Number.parseInt(amountToken, 10);\n const unitMs = DURATION_UNIT_MS[unitToken];\n if (unitMs === undefined || amount <= 0) {\n throw new Error(`Invalid duration \"${raw}\". Use forms like 30m, 6h, or 7d.`);\n }\n return amount * unitMs;\n}\n\n/** Converts a duration into an ISO timestamp suitable for `created_ats[gt]`. */\nexport function durationToCreatedAfter(raw: string, now: Date): string {\n return new Date(now.getTime() - parseDuration(raw)).toISOString();\n}\n\n/**\n * Parses a comma-separated `--type` filter. Accepts the shorthands `ssh` and\n * `crash`, or any full Cloud Foundry event type (e.g. `audit.app.start`).\n */\nexport function parseTypeFilter(raw: string | undefined): readonly string[] {\n if (raw === undefined) {\n return [];\n }\n\n const tokens = raw\n .split(\",\")\n .map((token) => token.trim())\n .filter((token) => token.length > 0);\n\n const resolved: string[] = [];\n for (const token of tokens) {\n if (token === \"ssh\") {\n resolved.push(...SSH_EVENT_TYPES);\n } else if (token === \"crash\") {\n resolved.push(...CRASH_EVENT_TYPES);\n } else if (token.startsWith(\"audit.\") || token.startsWith(\"app.\")) {\n resolved.push(token);\n } else {\n throw new Error(\n `Unknown event type \"${token}\". Use a full CF event type ` +\n '(e.g. audit.app.start) or the shorthand \"ssh\"/\"crash\".',\n );\n }\n }\n return [...new Set(resolved)];\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction readNumber(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nexport function toCrashRecord(event: AuditEvent): CrashRecord {\n return {\n at: event.createdAt,\n index: readNumber(event.data[\"index\"]),\n reason: readString(event.data[\"reason\"]) ?? readString(event.data[\"exit_description\"]),\n exitStatus: readNumber(event.data[\"exit_status\"]),\n };\n}\n\nexport function summarizeCrashes(appName: string, events: readonly AuditEvent[]): CrashSummary {\n const crashes = events\n .filter(isCrashEvent)\n .map(toCrashRecord)\n .sort((left, right) => right.at.localeCompare(left.at));\n const last = crashes[0];\n return {\n appName,\n crashCount: crashes.length,\n lastCrashAt: last?.at,\n lastCrashReason: last?.reason,\n crashes,\n };\n}\n\n\nfunction crashAppName(event: AuditEvent): string {\n if (event.target.name.length > 0) {\n return event.target.name;\n }\n if (event.target.guid.length > 0) {\n return event.target.guid;\n }\n return \"(unknown app)\";\n}\n\nexport function summarizeSpaceCrashes(\n selector: string,\n events: readonly AuditEvent[],\n): SpaceCrashSummary {\n const byApp = new Map<string, AuditEvent[]>();\n for (const event of events.filter(isCrashEvent)) {\n const appName = crashAppName(event);\n byApp.set(appName, [...(byApp.get(appName) ?? []), event]);\n }\n\n const apps: AppCrashSummary[] = [...byApp.entries()]\n .map(([appName, appEvents]) => summarizeCrashes(appName, appEvents))\n .map(({ appName, crashCount, lastCrashAt, lastCrashReason, crashes }) => ({\n appName,\n crashCount,\n lastCrashAt,\n lastCrashReason,\n crashes,\n }))\n .sort((left, right) => {\n const byTime = (right.lastCrashAt ?? \"\").localeCompare(left.lastCrashAt ?? \"\");\n return byTime === 0 ? left.appName.localeCompare(right.appName) : byTime;\n });\n\n return {\n scope: \"space\",\n selector,\n crashCount: apps.reduce((sum, app) => sum + app.crashCount, 0),\n lastCrashAt: apps[0]?.lastCrashAt,\n apps,\n };\n}\n","import type { AppHealth, AuditEvent, CrashReportSummary, CrashSummary, ProcessInstanceStat, SpaceCrashSummary, SshStatus } from \"./types.js\";\n\nconst EVENT_LABELS: Readonly<Record<string, string>> = {\n \"audit.app.create\": \"App created\",\n \"audit.app.update\": \"App updated\",\n \"audit.app.delete-request\": \"App delete requested\",\n \"audit.app.start\": \"App started\",\n \"audit.app.stop\": \"App stopped\",\n \"audit.app.restage\": \"App restaged\",\n \"audit.app.ssh-authorized\": \"SSH session authorized\",\n \"audit.app.ssh-unauthorized\": \"SSH attempt denied\",\n \"audit.app.crash\": \"App crashed\",\n \"audit.app.process.create\": \"Process created\",\n \"audit.app.process.scale\": \"Process scaled\",\n \"audit.app.process.update\": \"Process updated\",\n \"audit.app.process.crash\": \"Process crashed\",\n \"audit.app.process.terminate_instance\": \"Instance terminated\",\n \"audit.app.map-route\": \"Route mapped\",\n \"audit.app.unmap-route\": \"Route unmapped\",\n \"audit.app.environment_variables.show\": \"Env variables viewed\",\n};\n\nexport function describeEventType(type: string): string {\n return EVENT_LABELS[type] ?? type;\n}\n\nexport function formatUptime(seconds: number | undefined): string {\n if (seconds === undefined || seconds <= 0) {\n return \"-\";\n }\n const totalMinutes = Math.floor(seconds / 60);\n const days = Math.floor(totalMinutes / 1440);\n const hours = Math.floor((totalMinutes % 1440) / 60);\n const minutes = totalMinutes % 60;\n const parts: string[] = [];\n if (days > 0) {\n parts.push(`${days.toString()}d`);\n }\n if (hours > 0) {\n parts.push(`${hours.toString()}h`);\n }\n if (minutes > 0 || parts.length === 0) {\n parts.push(`${minutes.toString()}m`);\n }\n return parts.join(\" \");\n}\n\nexport function formatBytes(bytes: number | undefined): string {\n if (bytes === undefined || bytes < 0) {\n return \"-\";\n }\n const units = [\"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\"];\n let value = bytes;\n let unitIndex = 0;\n while (value >= 1024 && unitIndex < units.length - 1) {\n value /= 1024;\n unitIndex += 1;\n }\n return `${value.toFixed(unitIndex === 0 ? 0 : 1)} ${units[unitIndex] ?? \"B\"}`;\n}\n\nexport function formatCpu(cpu: number | undefined): string {\n if (cpu === undefined) {\n return \"-\";\n }\n return `${(cpu * 100).toFixed(1)}%`;\n}\n\nexport function formatRelativeTime(timestamp: string, now: Date): string {\n const at = Date.parse(timestamp);\n if (Number.isNaN(at)) {\n return \"unknown\";\n }\n const deltaMs = now.getTime() - at;\n if (deltaMs < 60_000) {\n return \"just now\";\n }\n const minutes = Math.floor(deltaMs / 60_000);\n if (minutes < 60) {\n return `${minutes.toString()}m ago`;\n }\n const hours = Math.floor(minutes / 60);\n if (hours < 24) {\n return `${hours.toString()}h ago`;\n }\n return `${Math.floor(hours / 24).toString()}d ago`;\n}\n\nexport function formatEventLine(event: AuditEvent, now: Date): string {\n const actor = event.actor.name.length > 0 ? event.actor.name : event.actor.type;\n return [\n ` ${event.createdAt.padEnd(26)}`,\n formatRelativeTime(event.createdAt, now).padEnd(11),\n describeEventType(event.type).padEnd(24),\n actor.length > 0 ? actor : \"(unknown actor)\",\n ].join(\" \");\n}\n\nexport function formatEventsReport(\n appName: string,\n events: readonly AuditEvent[],\n now: Date,\n): string {\n if (events.length === 0) {\n return `No audit events found for ${appName}.`;\n }\n return [\n `Audit events for ${appName} (${events.length.toString()}):`,\n ...events.map((event) => formatEventLine(event, now)),\n ].join(\"\\n\");\n}\n\n\nfunction eventTargetLabel(event: AuditEvent): string {\n if (event.target.name.length > 0) {\n return event.target.name;\n }\n if (event.target.type.length > 0) {\n return event.target.type;\n }\n return event.target.guid.length > 0 ? event.target.guid : \"(unknown target)\";\n}\n\nexport function formatSpaceEventLine(event: AuditEvent, now: Date): string {\n const actor = event.actor.name.length > 0 ? event.actor.name : event.actor.type;\n return [\n ` ${event.createdAt.padEnd(26)}`,\n formatRelativeTime(event.createdAt, now).padEnd(11),\n describeEventType(event.type).padEnd(24),\n eventTargetLabel(event).padEnd(18),\n actor.length > 0 ? actor : \"(unknown actor)\",\n ].join(\" \");\n}\n\nexport function formatSpaceEventsReport(\n selector: string,\n events: readonly AuditEvent[],\n now: Date,\n): string {\n if (events.length === 0) {\n return `No audit events found for space ${selector}.`;\n }\n return [\n `Audit events for space ${selector} (${events.length.toString()}):`,\n ...events.map((event) => formatSpaceEventLine(event, now)),\n ].join(\"\\n\");\n}\n\nexport function formatSshStatusReport(status: SshStatus, now: Date): string {\n const sessionLines = status.sessions.map((session) => {\n const tag = session.likelyActive ? \"[active]\" : \"[past] \";\n const when = formatRelativeTime(session.authorizedAt, now);\n return ` ${tag} ${session.actor.padEnd(30)} authorized ${session.authorizedAt} (${when})`;\n });\n const deniedLines = status.deniedAttempts.map((attempt) => {\n const actor = attempt.actor.name.length > 0 ? attempt.actor.name : attempt.actor.type;\n return ` ${actor.padEnd(30)} ${attempt.createdAt}`;\n });\n\n return [\n `SSH status for ${status.appName}`,\n \"\",\n ` SSH enabled: ${status.sshEnabled ? \"yes\" : \"no\"}`,\n ...(!status.sshEnabled && status.sshReason.length > 0\n ? [` Disabled reason: ${status.sshReason}`]\n : []),\n ` Recent SSH sessions: ${status.sessions.length.toString()}`,\n ` Likely active sessions: ${status.activeSessionCount.toString()}`,\n ...(sessionLines.length > 0 ? [\"\", \" Sessions:\", ...sessionLines] : []),\n ...(deniedLines.length > 0\n ? [\"\", ` Denied SSH attempts: ${status.deniedAttempts.length.toString()}`, ...deniedLines]\n : []),\n \"\",\n ' Note: Cloud Foundry exposes no live-session API; \"likely active\" sessions',\n \" are inferred from recent ssh-authorized audit events.\",\n ].join(\"\\n\");\n}\n\nfunction formatAppCrashReport(summary: CrashSummary, now: Date): string {\n if (summary.crashCount === 0) {\n return `No crashes found for ${summary.appName}.`;\n }\n const crashLines = summary.crashes.map((crash) => {\n const index = crash.index === undefined ? \"-\" : `#${crash.index.toString()}`;\n const reason = crash.reason ?? \"unknown\";\n const exit = crash.exitStatus === undefined ? \"\" : ` exit ${crash.exitStatus.toString()}`;\n return ` ${crash.at} instance ${index} ${reason}${exit}`;\n });\n\n return [\n `Crash report for ${summary.appName}`,\n \"\",\n ` Crashes: ${summary.crashCount.toString()}`,\n ...(summary.lastCrashAt === undefined\n ? []\n : [` Last crash: ${summary.lastCrashAt} (${formatRelativeTime(summary.lastCrashAt, now)})`]),\n ...(summary.lastCrashReason === undefined ? [] : [` Last reason: ${summary.lastCrashReason}`]),\n \"\",\n \" Recent crashes:\",\n ...crashLines,\n ].join(\"\\n\");\n}\n\nfunction formatSpaceCrashReport(summary: SpaceCrashSummary, now: Date): string {\n if (summary.crashCount === 0) {\n return `No crashes found for space ${summary.selector}.`;\n }\n const appLines = summary.apps.flatMap((app) => [\n ` ${app.appName} (${app.crashCount.toString()})`,\n ...app.crashes.map((crash) => {\n const index = crash.index === undefined ? \"-\" : `#${crash.index.toString()}`;\n const reason = crash.reason ?? \"unknown\";\n const exit = crash.exitStatus === undefined ? \"\" : ` exit ${crash.exitStatus.toString()}`;\n return ` ${crash.at} instance ${index} ${reason}${exit}`;\n }),\n ]);\n return [\n `Crash report for space ${summary.selector}`,\n \"\",\n ` Crashes: ${summary.crashCount.toString()}`,\n ` Apps affected: ${summary.apps.length.toString()}`,\n ...(summary.lastCrashAt === undefined\n ? []\n : [` Last crash: ${summary.lastCrashAt} (${formatRelativeTime(summary.lastCrashAt, now)})`]),\n \"\",\n ...appLines,\n ].join(\"\\n\");\n}\n\nexport function formatCrashReport(summary: CrashReportSummary, now: Date): string {\n if (\"scope\" in summary) {\n return formatSpaceCrashReport(summary, now);\n }\n return formatAppCrashReport(summary, now);\n}\n\nfunction formatInstanceLine(instance: ProcessInstanceStat): string {\n return [\n ` #${instance.index.toString()}`.padEnd(6),\n instance.state.padEnd(10),\n `up ${formatUptime(instance.uptimeSeconds)}`.padEnd(16),\n `cpu ${formatCpu(instance.cpu)}`.padEnd(13),\n `mem ${formatBytes(instance.memBytes)} / ${formatBytes(instance.memQuotaBytes)}`,\n ].join(\" \");\n}\n\nexport function formatStatusReport(health: AppHealth, now: Date): string {\n const lastEventLine =\n health.lastEvent === undefined\n ? \" Last event: (none)\"\n : ` Last event: ${describeEventType(health.lastEvent.type)} - ${health.lastEvent.createdAt} ` +\n `(${formatRelativeTime(health.lastEvent.createdAt, now)})`;\n\n return [\n `App status: ${health.appName}`,\n \"\",\n ` GUID: ${health.appGuid}`,\n ` Requested state: ${health.requestedState.length > 0 ? health.requestedState : \"unknown\"}`,\n ` SSH enabled: ${health.sshEnabled ? \"yes\" : \"no\"}`,\n ` Instances: ${health.instances.length.toString()}`,\n ...(health.instances.length > 0\n ? [\"\", ...health.instances.map((instance) => formatInstanceLine(instance))]\n : []),\n \"\",\n lastEventLine,\n ].join(\"\\n\");\n}\n","import { setTimeout as delayPromise } from \"node:timers/promises\";\n\nimport {\n fetchApp,\n fetchAuditEvents,\n fetchSshEnabled,\n fetchWebProcessStats,\n resolveOrganizationGuid,\n resolveSpaceGuid,\n} from \"./api.js\";\nimport type { CurlFn } from \"./api.js\";\nimport { cfAppGuid, cfCurl, prepareCfCliSession, withCfSession } from \"./cf.js\";\nimport { durationToCreatedAfter, summarizeCrashes, summarizeSpaceCrashes } from \"./events.js\";\nimport { resolveSelector } from \"./selector.js\";\nimport { buildSshStatus } from \"./ssh.js\";\nimport { CRASH_EVENT_TYPES, SSH_EVENT_TYPES } from \"./types.js\";\nimport type {\n AppHealth,\n AppSummary,\n AuditEvent,\n AuditEventScope,\n CfCredentials,\n CrashReportSummary,\n FetchAuditEventsInput,\n ProcessInstanceStat,\n ResolvedAppSelector,\n ResolvedSelector,\n SshEnabled,\n SshStatus,\n} from \"./types.js\";\n\n/** Typed accessor for the Cloud Foundry v3 API used by the runtime. */\nexport interface CfClient {\n readonly fetchAuditEvents: (input: FetchAuditEventsInput) => Promise<readonly AuditEvent[]>;\n readonly fetchApp: (appGuid: string) => Promise<AppSummary>;\n readonly fetchSshEnabled: (appGuid: string) => Promise<SshEnabled>;\n readonly fetchWebProcessStats: (appGuid: string) => Promise<readonly ProcessInstanceStat[]>;\n readonly resolveOrganizationGuid: (orgName: string) => Promise<string>;\n readonly resolveSpaceGuid: (spaceName: string, orgGuid: string) => Promise<string>;\n}\n\nexport interface CfTargetSession {\n readonly selector: ResolvedSelector;\n readonly client: CfClient;\n readonly resolveAppGuid: (appName: string) => Promise<string>;\n}\n\n/** Injection seam so the orchestration can be unit-tested without a real CF. */\nexport interface CfEventsDependencies {\n readonly resolveSelector: (raw: string) => Promise<ResolvedSelector>;\n readonly withCfTarget: <T>(\n selector: ResolvedSelector,\n credentials: CfCredentials,\n work: (session: CfTargetSession) => Promise<T>,\n ) => Promise<T>;\n readonly now: () => Date;\n}\n\nexport interface EventQueryOptions {\n readonly limit: number;\n readonly since: string | undefined;\n readonly types: readonly string[];\n}\n\nexport interface CrashQueryOptions {\n readonly limit: number;\n readonly since: string | undefined;\n}\n\nexport interface WatchOptions {\n readonly intervalMs: number;\n readonly lookback: string;\n readonly types: readonly string[];\n}\n\nconst SSH_STATUS_EVENT_LIMIT = 200;\nconst WATCH_FETCH_LIMIT = 100;\n\nfunction createCfClient(curl: CurlFn): CfClient {\n return {\n fetchAuditEvents: (input) => fetchAuditEvents(input, curl),\n fetchApp: (appGuid) => fetchApp(appGuid, curl),\n fetchSshEnabled: (appGuid) => fetchSshEnabled(appGuid, curl),\n fetchWebProcessStats: (appGuid) => fetchWebProcessStats(appGuid, curl),\n resolveOrganizationGuid: (orgName) => resolveOrganizationGuid(orgName, curl),\n resolveSpaceGuid: (spaceName, orgGuid) => resolveSpaceGuid(spaceName, orgGuid, curl),\n };\n}\n\nasync function defaultWithCfTarget<T>(\n selector: ResolvedSelector,\n credentials: CfCredentials,\n work: (session: CfTargetSession) => Promise<T>,\n): Promise<T> {\n return await withCfSession(async (ctx) => {\n await prepareCfCliSession(\n {\n apiEndpoint: selector.apiEndpoint,\n orgName: selector.orgName,\n spaceName: selector.spaceName,\n credentials,\n },\n ctx,\n );\n const client = createCfClient((path) => cfCurl(path, ctx));\n return await work({ selector, client, resolveAppGuid: (appName) => cfAppGuid(appName, ctx) });\n });\n}\n\nexport function createDefaultDependencies(): CfEventsDependencies {\n return {\n resolveSelector,\n withCfTarget: defaultWithCfTarget,\n now: () => new Date(),\n };\n}\n\nasync function delay(ms: number, signal: AbortSignal): Promise<void> {\n try {\n await delayPromise(ms, undefined, { signal });\n } catch {\n // The abort signal fired; resolve quietly so the watch loop can re-check it.\n }\n}\n\nfunction requireAppSelector(selector: ResolvedSelector, command: string): ResolvedAppSelector {\n if (selector.kind === \"app\") {\n return selector;\n }\n throw new Error(\n `The ${command} command requires an app selector. Use region/org/space/app or a bare app name.`,\n );\n}\n\nasync function resolveAuditScope(session: CfTargetSession): Promise<AuditEventScope> {\n if (session.selector.kind === \"app\") {\n return { kind: \"app\", appGuid: await session.resolveAppGuid(session.selector.appName) };\n }\n const orgGuid = await session.client.resolveOrganizationGuid(session.selector.orgName);\n return {\n kind: \"space\",\n spaceGuid: await session.client.resolveSpaceGuid(session.selector.spaceName, orgGuid),\n };\n}\n\n/** Orchestrates the cf-events commands on top of an injectable dependency set. */\nexport class CfEventsRuntime {\n private readonly deps: CfEventsDependencies;\n\n constructor(deps: CfEventsDependencies = createDefaultDependencies()) {\n this.deps = deps;\n }\n\n async fetchEvents(\n raw: string,\n credentials: CfCredentials,\n options: EventQueryOptions,\n ): Promise<readonly AuditEvent[]> {\n const selector = await this.deps.resolveSelector(raw);\n return await this.deps.withCfTarget(selector, credentials, async (session) => {\n return await session.client.fetchAuditEvents({\n scope: await resolveAuditScope(session),\n types: options.types.length > 0 ? options.types : undefined,\n createdAfter: this.resolveCreatedAfter(options.since),\n limit: options.limit,\n });\n });\n }\n\n async getSshStatus(raw: string, credentials: CfCredentials, since: string): Promise<SshStatus> {\n const selector = requireAppSelector(await this.deps.resolveSelector(raw), \"ssh-status\");\n return await this.deps.withCfTarget(selector, credentials, async ({ client, resolveAppGuid }) => {\n const appGuid = await resolveAppGuid(selector.appName);\n const [sshEnabled, events] = await Promise.all([\n client.fetchSshEnabled(appGuid),\n client.fetchAuditEvents({\n scope: { kind: \"app\", appGuid },\n types: [...SSH_EVENT_TYPES],\n createdAfter: durationToCreatedAfter(since, this.deps.now()),\n limit: SSH_STATUS_EVENT_LIMIT,\n }),\n ]);\n return buildSshStatus({\n appName: selector.appName,\n sshEnabled: sshEnabled.enabled,\n sshReason: sshEnabled.reason,\n events,\n now: this.deps.now(),\n });\n });\n }\n\n async getCrashes(\n raw: string,\n credentials: CfCredentials,\n options: CrashQueryOptions,\n ): Promise<CrashReportSummary> {\n const selector = await this.deps.resolveSelector(raw);\n return await this.deps.withCfTarget(selector, credentials, async (session) => {\n const events = await session.client.fetchAuditEvents({\n scope: await resolveAuditScope(session),\n types: [...CRASH_EVENT_TYPES],\n createdAfter: this.resolveCreatedAfter(options.since),\n limit: options.limit,\n });\n if (selector.kind === \"app\") {\n return summarizeCrashes(selector.appName, events);\n }\n return summarizeSpaceCrashes(`${selector.regionKey}/${selector.orgName}/${selector.spaceName}`, events);\n });\n }\n\n async getStatus(raw: string, credentials: CfCredentials): Promise<AppHealth> {\n const selector = requireAppSelector(await this.deps.resolveSelector(raw), \"status\");\n return await this.deps.withCfTarget(selector, credentials, async ({ client, resolveAppGuid }) => {\n const appGuid = await resolveAppGuid(selector.appName);\n const [app, instances, sshEnabled, recent] = await Promise.all([\n client.fetchApp(appGuid),\n client.fetchWebProcessStats(appGuid),\n client.fetchSshEnabled(appGuid),\n client.fetchAuditEvents({ scope: { kind: \"app\", appGuid }, types: undefined, createdAfter: undefined, limit: 1 }),\n ]);\n return {\n appName: selector.appName,\n appGuid,\n requestedState: app.state,\n sshEnabled: sshEnabled.enabled,\n instances,\n lastEvent: recent[0],\n };\n });\n }\n\n async watchEvents(\n raw: string,\n credentials: CfCredentials,\n options: WatchOptions,\n onEvent: (event: AuditEvent) => void,\n signal: AbortSignal,\n ): Promise<void> {\n const selector = await this.deps.resolveSelector(raw);\n await this.deps.withCfTarget(selector, credentials, async (session) => {\n const scope = await resolveAuditScope(session);\n const seen = new Set<string>();\n let cursor = durationToCreatedAfter(options.lookback, this.deps.now());\n\n while (!signal.aborted) {\n const events = await session.client.fetchAuditEvents({\n scope,\n types: options.types.length > 0 ? options.types : undefined,\n createdAfter: cursor,\n limit: WATCH_FETCH_LIMIT,\n });\n\n const fresh: AuditEvent[] = [];\n for (const event of events) {\n if (!seen.has(event.guid)) {\n seen.add(event.guid);\n fresh.push(event);\n }\n }\n for (const event of [...fresh].reverse()) {\n onEvent(event);\n }\n\n const newest = events[0];\n if (newest !== undefined && newest.createdAt.length > 0) {\n cursor = newest.createdAt;\n }\n await delay(options.intervalMs, signal);\n }\n });\n }\n\n private resolveCreatedAfter(since: string | undefined): string | undefined {\n return since === undefined ? undefined : durationToCreatedAfter(since, this.deps.now());\n }\n}\n","import type {\n AppSummary,\n AuditEvent,\n AuditEventScope,\n CfEntityRef,\n FetchAuditEventsInput,\n ProcessInstanceStat,\n SshEnabled,\n} from \"./types.js\";\n\n/** Calls a Cloud Foundry v3 API path and returns the raw response body. */\nexport type CurlFn = (path: string) => Promise<string>;\n\nconst MAX_PER_PAGE = 100;\n\nfunction parseJson(raw: string): unknown {\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n throw new Error(\"The CF API returned a response that is not valid JSON.\");\n }\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return {};\n}\n\nfunction asArray(value: unknown): readonly unknown[] {\n return Array.isArray(value) ? (value as readonly unknown[]) : [];\n}\n\nfunction asString(value: unknown): string {\n return typeof value === \"string\" ? value : \"\";\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction mapEntityRef(value: unknown): CfEntityRef {\n const record = asRecord(value);\n return {\n guid: asString(record[\"guid\"]),\n type: asString(record[\"type\"]),\n name: asString(record[\"name\"]),\n };\n}\n\nfunction optionalGuid(value: unknown): string | undefined {\n const guid = asString(asRecord(value)[\"guid\"]);\n return guid.length > 0 ? guid : undefined;\n}\n\nexport function mapAuditEvent(value: unknown): AuditEvent {\n const record = asRecord(value);\n return {\n guid: asString(record[\"guid\"]),\n type: asString(record[\"type\"]),\n createdAt: asString(record[\"created_at\"]),\n updatedAt: asString(record[\"updated_at\"]),\n actor: mapEntityRef(record[\"actor\"]),\n target: mapEntityRef(record[\"target\"]),\n data: asRecord(record[\"data\"]),\n spaceGuid: optionalGuid(record[\"space\"]),\n organizationGuid: optionalGuid(record[\"organization\"]),\n };\n}\n\nfunction mapProcessStat(value: unknown): ProcessInstanceStat {\n const record = asRecord(value);\n const usage = asRecord(record[\"usage\"]);\n return {\n type: asString(record[\"type\"]),\n index: asNumber(record[\"index\"]) ?? 0,\n state: asString(record[\"state\"]),\n uptimeSeconds: asNumber(record[\"uptime\"]),\n cpu: asNumber(usage[\"cpu\"]),\n memBytes: asNumber(usage[\"mem\"]),\n memQuotaBytes: asNumber(record[\"mem_quota\"]),\n diskBytes: asNumber(usage[\"disk\"]),\n diskQuotaBytes: asNumber(record[\"disk_quota\"]),\n };\n}\n\nexport function buildAuditEventsPath(input: FetchAuditEventsInput, perPage: number): string {\n const params = new URLSearchParams();\n applyAuditEventScope(params, input.scope);\n params.set(\"order_by\", \"-created_at\");\n params.set(\"per_page\", perPage.toString());\n if (input.types !== undefined && input.types.length > 0) {\n params.set(\"types\", input.types.join(\",\"));\n }\n if (input.createdAfter !== undefined) {\n params.set(\"created_ats[gt]\", input.createdAfter);\n }\n return `/v3/audit_events?${params.toString()}`;\n}\n\nfunction applyAuditEventScope(params: URLSearchParams, scope: AuditEventScope): void {\n if (scope.kind === \"app\") {\n params.set(\"target_guids\", scope.appGuid);\n return;\n }\n params.set(\"space_guids\", scope.spaceGuid);\n}\n\nexport async function resolveOrganizationGuid(orgName: string, curl: CurlFn): Promise<string> {\n const params = new URLSearchParams();\n params.set(\"names\", orgName);\n const body = asRecord(parseJson(await curl(`/v3/organizations?${params.toString()}`)));\n const first = asRecord(asArray(body[\"resources\"])[0]);\n const guid = asString(first[\"guid\"]);\n if (guid.length === 0) {\n throw new Error(`Could not resolve the GUID for organization \"${orgName}\".`);\n }\n return guid;\n}\n\nexport async function resolveSpaceGuid(\n spaceName: string,\n orgGuid: string,\n curl: CurlFn,\n): Promise<string> {\n const params = new URLSearchParams();\n params.set(\"names\", spaceName);\n params.set(\"organization_guids\", orgGuid);\n const body = asRecord(parseJson(await curl(`/v3/spaces?${params.toString()}`)));\n const first = asRecord(asArray(body[\"resources\"])[0]);\n const guid = asString(first[\"guid\"]);\n if (guid.length === 0) {\n throw new Error(`Could not resolve the GUID for space \"${spaceName}\".`);\n }\n return guid;\n}\n\nfunction nextPagePath(body: unknown): string | undefined {\n const next = asRecord(asRecord(asRecord(body)[\"pagination\"])[\"next\"]);\n const href = next[\"href\"];\n if (typeof href !== \"string\" || href.length === 0) {\n return undefined;\n }\n try {\n const url = new URL(href);\n return `${url.pathname}${url.search}`;\n } catch {\n return undefined;\n }\n}\n\n/** Fetches audit events for an app or space, following pagination up to `input.limit`. */\nexport async function fetchAuditEvents(\n input: FetchAuditEventsInput,\n curl: CurlFn,\n): Promise<readonly AuditEvent[]> {\n const limit = Math.max(input.limit, 1);\n const perPage = Math.min(limit, MAX_PER_PAGE);\n const events: AuditEvent[] = [];\n let path: string | undefined = buildAuditEventsPath(input, perPage);\n\n while (path !== undefined && events.length < limit) {\n const body = parseJson(await curl(path));\n for (const resource of asArray(asRecord(body)[\"resources\"])) {\n events.push(mapAuditEvent(resource));\n if (events.length >= limit) {\n break;\n }\n }\n path = events.length < limit ? nextPagePath(body) : undefined;\n }\n return events;\n}\n\nexport async function fetchApp(appGuid: string, curl: CurlFn): Promise<AppSummary> {\n const body = asRecord(parseJson(await curl(`/v3/apps/${appGuid}`)));\n return {\n guid: asString(body[\"guid\"]),\n name: asString(body[\"name\"]),\n state: asString(body[\"state\"]),\n };\n}\n\nexport async function fetchSshEnabled(appGuid: string, curl: CurlFn): Promise<SshEnabled> {\n const body = asRecord(parseJson(await curl(`/v3/apps/${appGuid}/ssh_enabled`)));\n return {\n enabled: body[\"enabled\"] === true,\n reason: asString(body[\"reason\"]),\n };\n}\n\nexport async function fetchWebProcessStats(\n appGuid: string,\n curl: CurlFn,\n): Promise<readonly ProcessInstanceStat[]> {\n const body = asRecord(parseJson(await curl(`/v3/apps/${appGuid}/processes/web/stats`)));\n return asArray(body[\"resources\"]).map(mapProcessStat);\n}\n","import { execFile } from \"node:child_process\";\nimport { mkdtemp, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\n\nimport type { CfCliContext, CfSessionInput } from \"./types.js\";\n\nconst execFileAsync = promisify(execFile);\n\nexport interface CurrentCfTarget {\n readonly apiEndpoint: string;\n readonly orgName: string;\n readonly spaceName: string;\n readonly regionKey?: string;\n}\n\nconst REGION_API_MAP: Record<string, string> = {\n ae01: \"https://api.cf.ae01.hana.ondemand.com\",\n ap01: \"https://api.cf.ap01.hana.ondemand.com\",\n ap10: \"https://api.cf.ap10.hana.ondemand.com\",\n ap11: \"https://api.cf.ap11.hana.ondemand.com\",\n ap12: \"https://api.cf.ap12.hana.ondemand.com\",\n ap20: \"https://api.cf.ap20.hana.ondemand.com\",\n ap21: \"https://api.cf.ap21.hana.ondemand.com\",\n ap30: \"https://api.cf.ap30.hana.ondemand.com\",\n br10: \"https://api.cf.br10.hana.ondemand.com\",\n br20: \"https://api.cf.br20.hana.ondemand.com\",\n br30: \"https://api.cf.br30.hana.ondemand.com\",\n ca10: \"https://api.cf.ca10.hana.ondemand.com\",\n ca20: \"https://api.cf.ca20.hana.ondemand.com\",\n ch20: \"https://api.cf.ch20.hana.ondemand.com\",\n cn20: \"https://api.cf.cn20.platform.sapcloud.cn\",\n cn40: \"https://api.cf.cn40.platform.sapcloud.cn\",\n eu01: \"https://api.cf.eu01.hana.ondemand.com\",\n eu02: \"https://api.cf.eu02.hana.ondemand.com\",\n eu10: \"https://api.cf.eu10.hana.ondemand.com\",\n \"eu10-002\": \"https://api.cf.eu10-002.hana.ondemand.com\",\n \"eu10-003\": \"https://api.cf.eu10-003.hana.ondemand.com\",\n \"eu10-004\": \"https://api.cf.eu10-004.hana.ondemand.com\",\n \"eu10-005\": \"https://api.cf.eu10-005.hana.ondemand.com\",\n \"eu10-006\": \"https://api.cf.eu10-006.hana.ondemand.com\",\n eu11: \"https://api.cf.eu11.hana.ondemand.com\",\n eu13: \"https://api.cf.eu13.hana.ondemand.com\",\n eu20: \"https://api.cf.eu20.hana.ondemand.com\",\n \"eu20-001\": \"https://api.cf.eu20-001.hana.ondemand.com\",\n eu30: \"https://api.cf.eu30.hana.ondemand.com\",\n in30: \"https://api.cf.in30.hana.ondemand.com\",\n jp10: \"https://api.cf.jp10.hana.ondemand.com\",\n jp20: \"https://api.cf.jp20.hana.ondemand.com\",\n jp30: \"https://api.cf.jp30.hana.ondemand.com\",\n jp31: \"https://api.cf.jp31.hana.ondemand.com\",\n sa30: \"https://api.cf.sa30.hana.ondemand.com\",\n us10: \"https://api.cf.us10.hana.ondemand.com\",\n \"us10-001\": \"https://api.cf.us10-001.hana.ondemand.com\",\n \"us10-002\": \"https://api.cf.us10-002.hana.ondemand.com\",\n us20: \"https://api.cf.us20.hana.ondemand.com\",\n us21: \"https://api.cf.us21.hana.ondemand.com\",\n us30: \"https://api.cf.us30.hana.ondemand.com\",\n // add more if needed, this covers common SAP BTP regions\n};\n\nexport function getApiEndpointForRegion(regionKey: string): string | undefined {\n return REGION_API_MAP[regionKey];\n}\n\nexport function getRegionKeyForApi(apiEndpoint: string): string | undefined {\n const normalized = apiEndpoint.trim().replace(/\\/+$/, \"\").toLowerCase();\n for (const [key, api] of Object.entries(REGION_API_MAP)) {\n if (api.toLowerCase() === normalized) {\n return key;\n }\n }\n return undefined;\n}\n\nexport async function readCurrentCfTarget(): Promise<CurrentCfTarget | undefined> {\n const cfBin = process.env[\"CF_EVENTS_CF_BIN\"] ?? \"cf\";\n const isScript = cfBin.endsWith(\".mjs\") || cfBin.endsWith(\".js\");\n const file = isScript ? \"node\" : cfBin;\n const args = isScript ? [cfBin, \"target\"] : [\"target\"];\n try {\n const { stdout } = await execFileAsync(file, args, {\n maxBuffer: 1024 * 1024,\n timeout: 10000,\n });\n return parseCfTargetOutput(stdout);\n } catch {\n return undefined;\n }\n}\n\nexport function parseCfTargetOutput(stdout: string): CurrentCfTarget | undefined {\n const fields = parseCfTargetFields(stdout);\n const apiEndpoint = fields.get(\"api endpoint\");\n const orgName = fields.get(\"org\");\n const spaceName = fields.get(\"space\");\n if (!apiEndpoint || !orgName || !spaceName) {\n return undefined;\n }\n const regionKey = getRegionKeyForApi(apiEndpoint);\n return {\n apiEndpoint,\n orgName,\n spaceName,\n ...(regionKey ? { regionKey } : {}),\n };\n}\n\nfunction parseCfTargetFields(stdout: string): Map<string, string> {\n const fields = new Map<string, string>();\n for (const line of stdout.split(/\\r?\\n/)) {\n const sep = line.indexOf(\":\");\n if (sep < 0) {\n continue;\n }\n const key = line.slice(0, sep).trim().toLowerCase();\n const value = line.slice(sep + 1).trim();\n if (key && value) {\n fields.set(key, value);\n }\n }\n return fields;\n}\n\nconst CF_MAX_BUFFER_BYTES = 16 * 1024 * 1024;\nconst CF_COMMAND_TIMEOUT_MS = 30_000;\nconst CF_RETRY_MAX_ATTEMPTS = 3;\nconst CF_RETRY_BASE_DELAY_MS = 120;\nconst GUID_PATTERN = /^[0-9a-f-]{16,}$/i;\n\ninterface ResolvedCommand {\n readonly bin: string;\n readonly argsPrefix: readonly string[];\n}\n\n/**\n * Runs `work` against an isolated, ephemeral `CF_HOME` so concurrent CF\n * sessions never share login state, and the directory is always cleaned up.\n */\nexport async function withCfSession<T>(work: (ctx: CfCliContext) => Promise<T>): Promise<T> {\n const cfHomeDir = await mkdtemp(join(tmpdir(), \"saptools-cf-events-\"));\n try {\n return await work({ cfHomeDir });\n } finally {\n await rm(cfHomeDir, { recursive: true, force: true });\n }\n}\n\n/** Authenticates the CF CLI and targets the requested org/space. */\nexport async function prepareCfCliSession(input: CfSessionInput, ctx: CfCliContext): Promise<void> {\n await runCfCommand([\"api\", input.apiEndpoint], ctx, \"Failed to set the CF API endpoint.\");\n await runCfCommand([\"auth\"], ctx, \"Failed to authenticate the Cloud Foundry CLI.\", {\n CF_USERNAME: input.credentials.email,\n CF_PASSWORD: input.credentials.password,\n });\n await runCfCommand(\n [\"target\", \"-o\", input.orgName, \"-s\", input.spaceName],\n ctx,\n \"Failed to target the CF org and space.\",\n );\n}\n\n/** Resolves the GUID of a targeted app via `cf app <name> --guid`. */\nexport async function cfAppGuid(appName: string, ctx: CfCliContext): Promise<string> {\n const stdout = await runCfCommand(\n [\"app\", appName, \"--guid\"],\n ctx,\n `Failed to resolve the GUID for app \"${appName}\".`,\n );\n const guid = stdout.trim();\n if (!GUID_PATTERN.test(guid)) {\n throw new Error(\n `Could not resolve the GUID for app \"${appName}\" - the CF CLI returned an unexpected value.`,\n );\n }\n return guid;\n}\n\n/** Calls a Cloud Foundry v3 API path via `cf curl` and returns the raw body. */\nexport async function cfCurl(path: string, ctx: CfCliContext): Promise<string> {\n return await runCfCommand([\"curl\", path], ctx, `Failed to call the CF API path \"${path}\".`);\n}\n\nexport async function runCfCommand(\n args: readonly string[],\n ctx: CfCliContext,\n failureMessage: string,\n envOverrides: Readonly<Record<string, string>> = {},\n): Promise<string> {\n const command = resolveCommand();\n const env = buildEnv(ctx.cfHomeDir, envOverrides);\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= CF_RETRY_MAX_ATTEMPTS; attempt += 1) {\n try {\n return await execFileWithResult(command.bin, [...command.argsPrefix, ...args], env);\n } catch (error) {\n lastError = error;\n if (attempt >= CF_RETRY_MAX_ATTEMPTS || !shouldRetryCfCliError(error)) {\n break;\n }\n await sleep(resolveRetryDelayMs(attempt));\n }\n }\n\n const detail = extractSafeCliDetail(lastError);\n throw new Error(detail.length > 0 ? `${failureMessage} ${detail}` : failureMessage, {\n cause: lastError,\n });\n}\n\nfunction resolveCommand(): ResolvedCommand {\n const bin = process.env[\"CF_EVENTS_CF_BIN\"] ?? \"cf\";\n if (/\\.(?:c|m)?js$/i.test(bin)) {\n return { bin: process.execPath, argsPrefix: [bin] };\n }\n return { bin, argsPrefix: [] };\n}\n\nfunction buildEnv(cfHomeDir: string, overrides: Readonly<Record<string, string>>): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = { ...process.env };\n delete env[\"SAP_EMAIL\"];\n delete env[\"SAP_PASSWORD\"];\n env[\"CF_HOME\"] = cfHomeDir;\n Object.assign(env, overrides);\n return env;\n}\n\nasync function execFileWithResult(\n command: string,\n args: readonly string[],\n env: NodeJS.ProcessEnv,\n): Promise<string> {\n return await new Promise<string>((resolvePromise, rejectPromise) => {\n execFile(\n command,\n [...args],\n { env, maxBuffer: CF_MAX_BUFFER_BYTES, timeout: CF_COMMAND_TIMEOUT_MS },\n (error: Error | null, stdout: string, stderr: string) => {\n if (error !== null) {\n rejectPromise(Object.assign(error, { stdout, stderr }));\n return;\n }\n resolvePromise(stdout);\n },\n );\n });\n}\n\nfunction shouldRetryCfCliError(error: unknown): boolean {\n const haystack = `${readErrorStderr(error)} ${readErrorMessage(error)}`.toLowerCase();\n if (\n haystack.includes(\"invalid\") ||\n haystack.includes(\"credentials were rejected\") ||\n haystack.includes(\"authentication failed\") ||\n haystack.includes(\"not authorized\") ||\n haystack.includes(\"forbidden\")\n ) {\n return false;\n }\n return (\n haystack.includes(\"timeout\") ||\n haystack.includes(\"timed out\") ||\n haystack.includes(\"connection reset\") ||\n haystack.includes(\"temporarily unavailable\") ||\n haystack.includes(\"network\") ||\n haystack.includes(\"econnreset\") ||\n haystack.includes(\"econnrefused\")\n );\n}\n\nfunction readErrorStderr(error: unknown): string {\n if (typeof error !== \"object\" || error === null) {\n return \"\";\n }\n const stderr = (error as { readonly stderr?: unknown }).stderr;\n return typeof stderr === \"string\" ? stderr : \"\";\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : \"\";\n}\n\nfunction resolveRetryDelayMs(attempt: number): number {\n const jitter = Math.floor(Math.random() * CF_RETRY_BASE_DELAY_MS);\n return CF_RETRY_BASE_DELAY_MS * attempt + jitter;\n}\n\nasync function sleep(delayMs: number): Promise<void> {\n await new Promise<void>((resolvePromise) => {\n setTimeout(resolvePromise, delayMs);\n });\n}\n\nfunction extractSafeCliDetail(error: unknown): string {\n const stderr = readErrorStderr(error);\n if (stderr.length === 0) {\n return \"\";\n }\n const cleaned = stderr.replaceAll(/\\s+/g, \" \").trim();\n return cleaned.length > 0 ? `(cli: ${cleaned.slice(0, 180)})` : \"\";\n}\n","import { getApiEndpointForRegion, readCurrentCfTarget } from \"./cf.js\";\nimport type { ResolvedSelector } from \"./types.js\";\n\nexport interface ParsedAppName {\n readonly kind: \"appName\";\n readonly appName: string;\n}\n\nexport interface ParsedAppPath {\n readonly kind: \"appPath\";\n readonly regionKey: string;\n readonly orgName: string;\n readonly spaceName: string;\n readonly appName: string;\n}\n\nexport interface ParsedSpacePath {\n readonly kind: \"spacePath\";\n readonly regionKey: string;\n readonly orgName: string;\n readonly spaceName: string;\n}\n\nexport type ParsedSelector = ParsedAppName | ParsedAppPath | ParsedSpacePath;\n\nconst SELECTOR_USAGE = 'use \"region/org/space\", \"region/org/space/app\", or a bare app name';\n\nfunction assertNonEmpty(raw: string, parts: readonly string[], shape: string): void {\n if (parts.some((part) => part.length === 0)) {\n throw new Error(`Invalid selector \"${raw}\": every ${shape} segment must be non-empty.`);\n }\n}\n\n/** Parses a raw selector string into its structural parts without any I/O. */\nexport function parseSelector(raw: string): ParsedSelector {\n const trimmed = raw.trim();\n if (trimmed.length === 0) {\n throw new Error(`A selector is required: ${SELECTOR_USAGE}.`);\n }\n\n const parts = trimmed.split(\"/\").map((part) => part.trim());\n\n if (parts.length === 1) {\n const appName = parts[0] ?? \"\";\n if (appName.length === 0) {\n throw new Error(`A selector is required: ${SELECTOR_USAGE}.`);\n }\n return { kind: \"appName\", appName };\n }\n\n if (parts.length === 3) {\n assertNonEmpty(raw, parts, \"region/org/space\");\n const [regionKey, orgName, spaceName] = parts;\n return { kind: \"spacePath\", regionKey: regionKey ?? \"\", orgName: orgName ?? \"\", spaceName: spaceName ?? \"\" };\n }\n\n if (parts.length === 4) {\n assertNonEmpty(raw, parts, \"region/org/space/app\");\n const [regionKey, orgName, spaceName, appName] = parts;\n return {\n kind: \"appPath\",\n regionKey: regionKey ?? \"\",\n orgName: orgName ?? \"\",\n spaceName: spaceName ?? \"\",\n appName: appName ?? \"\",\n };\n }\n\n throw new Error(`Invalid selector \"${raw}\": ${SELECTOR_USAGE}.`);\n}\n\n/**\n * Resolves a raw selector.\n * - Bare app name: based strictly on the CURRENT CF target (no global search).\n * - Explicit paths: use known region-to-api mapping; trust the provided org/space/app.\n */\nexport async function resolveSelector(raw: string): Promise<ResolvedSelector> {\n const parsed = parseSelector(raw);\n\n if (parsed.kind === \"appName\") {\n const current = await readCurrentCfTarget();\n if (!current) {\n throw new Error(\n \"No current CF target found. Run `cf target -o <org> -s <space>` or pass a full region/org/space/app selector.\",\n );\n }\n return {\n kind: \"app\",\n raw,\n regionKey: current.regionKey ?? \"\",\n apiEndpoint: current.apiEndpoint,\n orgName: current.orgName,\n spaceName: current.spaceName,\n appName: parsed.appName,\n };\n }\n\n const apiEndpoint = getApiEndpointForRegion(parsed.regionKey);\n if (!apiEndpoint) {\n throw new Error(\n `Unknown region \"${parsed.regionKey}\". Use a bare app name (requires current CF target) or a known region key.`,\n );\n }\n\n if (parsed.kind === \"spacePath\") {\n return {\n kind: \"space\",\n raw,\n regionKey: parsed.regionKey,\n apiEndpoint,\n orgName: parsed.orgName,\n spaceName: parsed.spaceName,\n };\n }\n\n return {\n kind: \"app\",\n raw,\n regionKey: parsed.regionKey,\n apiEndpoint,\n orgName: parsed.orgName,\n spaceName: parsed.spaceName,\n appName: parsed.appName,\n };\n}\n","import { isSshEvent } from \"./events.js\";\nimport type { AuditEvent, SshSession, SshStatus } from \"./types.js\";\n\nconst SSH_AUTHORIZED_TYPE = \"audit.app.ssh-authorized\";\nconst SSH_DENIED_TYPE = \"audit.app.ssh-unauthorized\";\n\n/**\n * Window after an `ssh-authorized` event during which the session is treated\n * as \"likely active\". Cloud Foundry emits no session-close event, so this is\n * a heuristic rather than a live-session check.\n */\nconst LIKELY_ACTIVE_WINDOW_MS = 60 * 60 * 1000;\n\nfunction isWithinActiveWindow(timestamp: string, now: Date): boolean {\n const at = Date.parse(timestamp);\n if (Number.isNaN(at)) {\n return false;\n }\n const delta = now.getTime() - at;\n return delta >= 0 && delta <= LIKELY_ACTIVE_WINDOW_MS;\n}\n\n/** Infers SSH/debug sessions from `ssh-authorized` audit events. */\nexport function inferSshSessions(events: readonly AuditEvent[], now: Date): readonly SshSession[] {\n return events\n .filter((event) => event.type === SSH_AUTHORIZED_TYPE)\n .map((event) => ({\n actor: event.actor.name.length > 0 ? event.actor.name : event.actor.guid,\n authorizedAt: event.createdAt,\n likelyActive: isWithinActiveWindow(event.createdAt, now),\n }))\n .sort((left, right) => right.authorizedAt.localeCompare(left.authorizedAt));\n}\n\nexport interface SshStatusInput {\n readonly appName: string;\n readonly sshEnabled: boolean;\n readonly sshReason: string;\n readonly events: readonly AuditEvent[];\n readonly now: Date;\n}\n\nexport function buildSshStatus(input: SshStatusInput): SshStatus {\n const sshEvents = input.events.filter(isSshEvent);\n const sessions = inferSshSessions(sshEvents, input.now);\n const deniedAttempts = sshEvents\n .filter((event) => event.type === SSH_DENIED_TYPE)\n .sort((left, right) => right.createdAt.localeCompare(left.createdAt));\n\n return {\n appName: input.appName,\n sshEnabled: input.sshEnabled,\n sshReason: input.sshReason,\n sessions,\n deniedAttempts,\n activeSessionCount: sessions.filter((session) => session.likelyActive).length,\n };\n}\n"],"mappings":";;;AAAA,SAAS,cAAc,oBAAoB;AAC3C,SAAS,SAAS,QAAAA,OAAM,eAAe;AACvC,OAAOC,cAAa;AACpB,SAAS,qBAAqB;AAE9B,SAAS,eAAe;;;ACqBjB,IAAM,kBAAkB,CAAC,4BAA4B,4BAA4B;AAGjF,IAAM,oBAAoB,CAAC,mBAAmB,yBAAyB;;;AC1B9E,IAAM,mBAAmB;AAEzB,IAAM,mBAAqD;AAAA,EACzD,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACL;AAEO,SAAS,aAAa,OAA4B;AACvD,SAAQ,kBAAwC,SAAS,MAAM,IAAI;AACrE;AAEO,SAAS,WAAW,OAA4B;AACrD,SAAQ,gBAAsC,SAAS,MAAM,IAAI;AACnE;AAkBO,SAAS,cAAc,KAAqB;AACjD,QAAM,QAAQ,iBAAiB,KAAK,IAAI,KAAK,EAAE,YAAY,CAAC;AAC5D,QAAM,cAAc,QAAQ,CAAC;AAC7B,QAAM,YAAY,QAAQ,CAAC;AAC3B,MAAI,gBAAgB,UAAa,cAAc,QAAW;AACxD,UAAM,IAAI,MAAM,qBAAqB,GAAG,mCAAmC;AAAA,EAC7E;AAEA,QAAM,SAAS,OAAO,SAAS,aAAa,EAAE;AAC9C,QAAM,SAAS,iBAAiB,SAAS;AACzC,MAAI,WAAW,UAAa,UAAU,GAAG;AACvC,UAAM,IAAI,MAAM,qBAAqB,GAAG,mCAAmC;AAAA,EAC7E;AACA,SAAO,SAAS;AAClB;AAGO,SAAS,uBAAuB,KAAa,KAAmB;AACrE,SAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,cAAc,GAAG,CAAC,EAAE,YAAY;AAClE;AAMO,SAAS,gBAAgB,KAA4C;AAC1E,MAAI,QAAQ,QAAW;AACrB,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,SAAS,IACZ,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AAErC,QAAM,WAAqB,CAAC;AAC5B,aAAW,SAAS,QAAQ;AAC1B,QAAI,UAAU,OAAO;AACnB,eAAS,KAAK,GAAG,eAAe;AAAA,IAClC,WAAW,UAAU,SAAS;AAC5B,eAAS,KAAK,GAAG,iBAAiB;AAAA,IACpC,WAAW,MAAM,WAAW,QAAQ,KAAK,MAAM,WAAW,MAAM,GAAG;AACjE,eAAS,KAAK,KAAK;AAAA,IACrB,OAAO;AACL,YAAM,IAAI;AAAA,QACR,uBAAuB,KAAK;AAAA,MAE9B;AAAA,IACF;AAAA,EACF;AACA,SAAO,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC;AAC9B;AAEA,SAAS,WAAW,OAAoC;AACtD,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AACjE;AAEA,SAAS,WAAW,OAAoC;AACtD,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,QAAQ;AACvE;AAEO,SAAS,cAAc,OAAgC;AAC5D,SAAO;AAAA,IACL,IAAI,MAAM;AAAA,IACV,OAAO,WAAW,MAAM,KAAK,OAAO,CAAC;AAAA,IACrC,QAAQ,WAAW,MAAM,KAAK,QAAQ,CAAC,KAAK,WAAW,MAAM,KAAK,kBAAkB,CAAC;AAAA,IACrF,YAAY,WAAW,MAAM,KAAK,aAAa,CAAC;AAAA,EAClD;AACF;AAEO,SAAS,iBAAiB,SAAiB,QAA6C;AAC7F,QAAM,UAAU,OACb,OAAO,YAAY,EACnB,IAAI,aAAa,EACjB,KAAK,CAAC,MAAM,UAAU,MAAM,GAAG,cAAc,KAAK,EAAE,CAAC;AACxD,QAAM,OAAO,QAAQ,CAAC;AACtB,SAAO;AAAA,IACL;AAAA,IACA,YAAY,QAAQ;AAAA,IACpB,aAAa,MAAM;AAAA,IACnB,iBAAiB,MAAM;AAAA,IACvB;AAAA,EACF;AACF;AAGA,SAAS,aAAa,OAA2B;AAC/C,MAAI,MAAM,OAAO,KAAK,SAAS,GAAG;AAChC,WAAO,MAAM,OAAO;AAAA,EACtB;AACA,MAAI,MAAM,OAAO,KAAK,SAAS,GAAG;AAChC,WAAO,MAAM,OAAO;AAAA,EACtB;AACA,SAAO;AACT;AAEO,SAAS,sBACd,UACA,QACmB;AACnB,QAAM,QAAQ,oBAAI,IAA0B;AAC5C,aAAW,SAAS,OAAO,OAAO,YAAY,GAAG;AAC/C,UAAM,UAAU,aAAa,KAAK;AAClC,UAAM,IAAI,SAAS,CAAC,GAAI,MAAM,IAAI,OAAO,KAAK,CAAC,GAAI,KAAK,CAAC;AAAA,EAC3D;AAEA,QAAM,OAA0B,CAAC,GAAG,MAAM,QAAQ,CAAC,EAChD,IAAI,CAAC,CAAC,SAAS,SAAS,MAAM,iBAAiB,SAAS,SAAS,CAAC,EAClE,IAAI,CAAC,EAAE,SAAS,YAAY,aAAa,iBAAiB,QAAQ,OAAO;AAAA,IACxE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,EACD,KAAK,CAAC,MAAM,UAAU;AACrB,UAAM,UAAU,MAAM,eAAe,IAAI,cAAc,KAAK,eAAe,EAAE;AAC7E,WAAO,WAAW,IAAI,KAAK,QAAQ,cAAc,MAAM,OAAO,IAAI;AAAA,EACpE,CAAC;AAEH,SAAO;AAAA,IACL,OAAO;AAAA,IACP;AAAA,IACA,YAAY,KAAK,OAAO,CAAC,KAAK,QAAQ,MAAM,IAAI,YAAY,CAAC;AAAA,IAC7D,aAAa,KAAK,CAAC,GAAG;AAAA,IACtB;AAAA,EACF;AACF;;;ACjKA,IAAM,eAAiD;AAAA,EACrD,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,4BAA4B;AAAA,EAC5B,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,4BAA4B;AAAA,EAC5B,8BAA8B;AAAA,EAC9B,mBAAmB;AAAA,EACnB,4BAA4B;AAAA,EAC5B,2BAA2B;AAAA,EAC3B,4BAA4B;AAAA,EAC5B,2BAA2B;AAAA,EAC3B,wCAAwC;AAAA,EACxC,uBAAuB;AAAA,EACvB,yBAAyB;AAAA,EACzB,wCAAwC;AAC1C;AAEO,SAAS,kBAAkB,MAAsB;AACtD,SAAO,aAAa,IAAI,KAAK;AAC/B;AAEO,SAAS,aAAa,SAAqC;AAChE,MAAI,YAAY,UAAa,WAAW,GAAG;AACzC,WAAO;AAAA,EACT;AACA,QAAM,eAAe,KAAK,MAAM,UAAU,EAAE;AAC5C,QAAM,OAAO,KAAK,MAAM,eAAe,IAAI;AAC3C,QAAM,QAAQ,KAAK,MAAO,eAAe,OAAQ,EAAE;AACnD,QAAM,UAAU,eAAe;AAC/B,QAAM,QAAkB,CAAC;AACzB,MAAI,OAAO,GAAG;AACZ,UAAM,KAAK,GAAG,KAAK,SAAS,CAAC,GAAG;AAAA,EAClC;AACA,MAAI,QAAQ,GAAG;AACb,UAAM,KAAK,GAAG,MAAM,SAAS,CAAC,GAAG;AAAA,EACnC;AACA,MAAI,UAAU,KAAK,MAAM,WAAW,GAAG;AACrC,UAAM,KAAK,GAAG,QAAQ,SAAS,CAAC,GAAG;AAAA,EACrC;AACA,SAAO,MAAM,KAAK,GAAG;AACvB;AAEO,SAAS,YAAY,OAAmC;AAC7D,MAAI,UAAU,UAAa,QAAQ,GAAG;AACpC,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,CAAC,KAAK,OAAO,OAAO,OAAO,KAAK;AAC9C,MAAI,QAAQ;AACZ,MAAI,YAAY;AAChB,SAAO,SAAS,QAAQ,YAAY,MAAM,SAAS,GAAG;AACpD,aAAS;AACT,iBAAa;AAAA,EACf;AACA,SAAO,GAAG,MAAM,QAAQ,cAAc,IAAI,IAAI,CAAC,CAAC,IAAI,MAAM,SAAS,KAAK,GAAG;AAC7E;AAEO,SAAS,UAAU,KAAiC;AACzD,MAAI,QAAQ,QAAW;AACrB,WAAO;AAAA,EACT;AACA,SAAO,IAAI,MAAM,KAAK,QAAQ,CAAC,CAAC;AAClC;AAEO,SAAS,mBAAmB,WAAmB,KAAmB;AACvE,QAAM,KAAK,KAAK,MAAM,SAAS;AAC/B,MAAI,OAAO,MAAM,EAAE,GAAG;AACpB,WAAO;AAAA,EACT;AACA,QAAM,UAAU,IAAI,QAAQ,IAAI;AAChC,MAAI,UAAU,KAAQ;AACpB,WAAO;AAAA,EACT;AACA,QAAM,UAAU,KAAK,MAAM,UAAU,GAAM;AAC3C,MAAI,UAAU,IAAI;AAChB,WAAO,GAAG,QAAQ,SAAS,CAAC;AAAA,EAC9B;AACA,QAAM,QAAQ,KAAK,MAAM,UAAU,EAAE;AACrC,MAAI,QAAQ,IAAI;AACd,WAAO,GAAG,MAAM,SAAS,CAAC;AAAA,EAC5B;AACA,SAAO,GAAG,KAAK,MAAM,QAAQ,EAAE,EAAE,SAAS,CAAC;AAC7C;AAEO,SAAS,gBAAgB,OAAmB,KAAmB;AACpE,QAAM,QAAQ,MAAM,MAAM,KAAK,SAAS,IAAI,MAAM,MAAM,OAAO,MAAM,MAAM;AAC3E,SAAO;AAAA,IACL,KAAK,MAAM,UAAU,OAAO,EAAE,CAAC;AAAA,IAC/B,mBAAmB,MAAM,WAAW,GAAG,EAAE,OAAO,EAAE;AAAA,IAClD,kBAAkB,MAAM,IAAI,EAAE,OAAO,EAAE;AAAA,IACvC,MAAM,SAAS,IAAI,QAAQ;AAAA,EAC7B,EAAE,KAAK,IAAI;AACb;AAEO,SAAS,mBACd,SACA,QACA,KACQ;AACR,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,6BAA6B,OAAO;AAAA,EAC7C;AACA,SAAO;AAAA,IACL,oBAAoB,OAAO,KAAK,OAAO,OAAO,SAAS,CAAC;AAAA,IACxD,GAAG,OAAO,IAAI,CAAC,UAAU,gBAAgB,OAAO,GAAG,CAAC;AAAA,EACtD,EAAE,KAAK,IAAI;AACb;AAGA,SAAS,iBAAiB,OAA2B;AACnD,MAAI,MAAM,OAAO,KAAK,SAAS,GAAG;AAChC,WAAO,MAAM,OAAO;AAAA,EACtB;AACA,MAAI,MAAM,OAAO,KAAK,SAAS,GAAG;AAChC,WAAO,MAAM,OAAO;AAAA,EACtB;AACA,SAAO,MAAM,OAAO,KAAK,SAAS,IAAI,MAAM,OAAO,OAAO;AAC5D;AAEO,SAAS,qBAAqB,OAAmB,KAAmB;AACzE,QAAM,QAAQ,MAAM,MAAM,KAAK,SAAS,IAAI,MAAM,MAAM,OAAO,MAAM,MAAM;AAC3E,SAAO;AAAA,IACL,KAAK,MAAM,UAAU,OAAO,EAAE,CAAC;AAAA,IAC/B,mBAAmB,MAAM,WAAW,GAAG,EAAE,OAAO,EAAE;AAAA,IAClD,kBAAkB,MAAM,IAAI,EAAE,OAAO,EAAE;AAAA,IACvC,iBAAiB,KAAK,EAAE,OAAO,EAAE;AAAA,IACjC,MAAM,SAAS,IAAI,QAAQ;AAAA,EAC7B,EAAE,KAAK,IAAI;AACb;AAEO,SAAS,wBACd,UACA,QACA,KACQ;AACR,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,mCAAmC,QAAQ;AAAA,EACpD;AACA,SAAO;AAAA,IACL,0BAA0B,QAAQ,KAAK,OAAO,OAAO,SAAS,CAAC;AAAA,IAC/D,GAAG,OAAO,IAAI,CAAC,UAAU,qBAAqB,OAAO,GAAG,CAAC;AAAA,EAC3D,EAAE,KAAK,IAAI;AACb;AAEO,SAAS,sBAAsB,QAAmB,KAAmB;AAC1E,QAAM,eAAe,OAAO,SAAS,IAAI,CAAC,YAAY;AACpD,UAAM,MAAM,QAAQ,eAAe,aAAa;AAChD,UAAM,OAAO,mBAAmB,QAAQ,cAAc,GAAG;AACzD,WAAO,OAAO,GAAG,KAAK,QAAQ,MAAM,OAAO,EAAE,CAAC,gBAAgB,QAAQ,YAAY,KAAK,IAAI;AAAA,EAC7F,CAAC;AACD,QAAM,cAAc,OAAO,eAAe,IAAI,CAAC,YAAY;AACzD,UAAM,QAAQ,QAAQ,MAAM,KAAK,SAAS,IAAI,QAAQ,MAAM,OAAO,QAAQ,MAAM;AACjF,WAAO,OAAO,MAAM,OAAO,EAAE,CAAC,KAAK,QAAQ,SAAS;AAAA,EACtD,CAAC;AAED,SAAO;AAAA,IACL,kBAAkB,OAAO,OAAO;AAAA,IAChC;AAAA,IACA,6BAA6B,OAAO,aAAa,QAAQ,IAAI;AAAA,IAC7D,GAAI,CAAC,OAAO,cAAc,OAAO,UAAU,SAAS,IAChD,CAAC,6BAA6B,OAAO,SAAS,EAAE,IAChD,CAAC;AAAA,IACL,6BAA6B,OAAO,SAAS,OAAO,SAAS,CAAC;AAAA,IAC9D,6BAA6B,OAAO,mBAAmB,SAAS,CAAC;AAAA,IACjE,GAAI,aAAa,SAAS,IAAI,CAAC,IAAI,eAAe,GAAG,YAAY,IAAI,CAAC;AAAA,IACtE,GAAI,YAAY,SAAS,IACrB,CAAC,IAAI,0BAA0B,OAAO,eAAe,OAAO,SAAS,CAAC,IAAI,GAAG,WAAW,IACxF,CAAC;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEA,SAAS,qBAAqB,SAAuB,KAAmB;AACtE,MAAI,QAAQ,eAAe,GAAG;AAC5B,WAAO,wBAAwB,QAAQ,OAAO;AAAA,EAChD;AACA,QAAM,aAAa,QAAQ,QAAQ,IAAI,CAAC,UAAU;AAChD,UAAM,QAAQ,MAAM,UAAU,SAAY,MAAM,IAAI,MAAM,MAAM,SAAS,CAAC;AAC1E,UAAM,SAAS,MAAM,UAAU;AAC/B,UAAM,OAAO,MAAM,eAAe,SAAY,KAAK,UAAU,MAAM,WAAW,SAAS,CAAC;AACxF,WAAO,OAAO,MAAM,EAAE,cAAc,KAAK,KAAK,MAAM,GAAG,IAAI;AAAA,EAC7D,CAAC;AAED,SAAO;AAAA,IACL,oBAAoB,QAAQ,OAAO;AAAA,IACnC;AAAA,IACA,cAAc,QAAQ,WAAW,SAAS,CAAC;AAAA,IAC3C,GAAI,QAAQ,gBAAgB,SACxB,CAAC,IACD,CAAC,iBAAiB,QAAQ,WAAW,KAAK,mBAAmB,QAAQ,aAAa,GAAG,CAAC,GAAG;AAAA,IAC7F,GAAI,QAAQ,oBAAoB,SAAY,CAAC,IAAI,CAAC,kBAAkB,QAAQ,eAAe,EAAE;AAAA,IAC7F;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,EAAE,KAAK,IAAI;AACb;AAEA,SAAS,uBAAuB,SAA4B,KAAmB;AAC7E,MAAI,QAAQ,eAAe,GAAG;AAC5B,WAAO,8BAA8B,QAAQ,QAAQ;AAAA,EACvD;AACA,QAAM,WAAW,QAAQ,KAAK,QAAQ,CAAC,QAAQ;AAAA,IAC7C,KAAK,IAAI,OAAO,KAAK,IAAI,WAAW,SAAS,CAAC;AAAA,IAC9C,GAAG,IAAI,QAAQ,IAAI,CAAC,UAAU;AAC5B,YAAM,QAAQ,MAAM,UAAU,SAAY,MAAM,IAAI,MAAM,MAAM,SAAS,CAAC;AAC1E,YAAM,SAAS,MAAM,UAAU;AAC/B,YAAM,OAAO,MAAM,eAAe,SAAY,KAAK,UAAU,MAAM,WAAW,SAAS,CAAC;AACxF,aAAO,OAAO,MAAM,EAAE,cAAc,KAAK,KAAK,MAAM,GAAG,IAAI;AAAA,IAC7D,CAAC;AAAA,EACH,CAAC;AACD,SAAO;AAAA,IACL,0BAA0B,QAAQ,QAAQ;AAAA,IAC1C;AAAA,IACA,cAAc,QAAQ,WAAW,SAAS,CAAC;AAAA,IAC3C,oBAAoB,QAAQ,KAAK,OAAO,SAAS,CAAC;AAAA,IAClD,GAAI,QAAQ,gBAAgB,SACxB,CAAC,IACD,CAAC,iBAAiB,QAAQ,WAAW,KAAK,mBAAmB,QAAQ,aAAa,GAAG,CAAC,GAAG;AAAA,IAC7F;AAAA,IACA,GAAG;AAAA,EACL,EAAE,KAAK,IAAI;AACb;AAEO,SAAS,kBAAkB,SAA6B,KAAmB;AAChF,MAAI,WAAW,SAAS;AACtB,WAAO,uBAAuB,SAAS,GAAG;AAAA,EAC5C;AACA,SAAO,qBAAqB,SAAS,GAAG;AAC1C;AAEA,SAAS,mBAAmB,UAAuC;AACjE,SAAO;AAAA,IACL,MAAM,SAAS,MAAM,SAAS,CAAC,GAAG,OAAO,CAAC;AAAA,IAC1C,SAAS,MAAM,OAAO,EAAE;AAAA,IACxB,MAAM,aAAa,SAAS,aAAa,CAAC,GAAG,OAAO,EAAE;AAAA,IACtD,OAAO,UAAU,SAAS,GAAG,CAAC,GAAG,OAAO,EAAE;AAAA,IAC1C,OAAO,YAAY,SAAS,QAAQ,CAAC,MAAM,YAAY,SAAS,aAAa,CAAC;AAAA,EAChF,EAAE,KAAK,IAAI;AACb;AAEO,SAAS,mBAAmB,QAAmB,KAAmB;AACvE,QAAM,gBACJ,OAAO,cAAc,SACjB,yBACA,iBAAiB,kBAAkB,OAAO,UAAU,IAAI,CAAC,MAAM,OAAO,UAAU,SAAS,KACrF,mBAAmB,OAAO,UAAU,WAAW,GAAG,CAAC;AAE7D,SAAO;AAAA,IACL,eAAe,OAAO,OAAO;AAAA,IAC7B;AAAA,IACA,sBAAsB,OAAO,OAAO;AAAA,IACpC,sBAAsB,OAAO,eAAe,SAAS,IAAI,OAAO,iBAAiB,SAAS;AAAA,IAC1F,sBAAsB,OAAO,aAAa,QAAQ,IAAI;AAAA,IACtD,sBAAsB,OAAO,UAAU,OAAO,SAAS,CAAC;AAAA,IACxD,GAAI,OAAO,UAAU,SAAS,IAC1B,CAAC,IAAI,GAAG,OAAO,UAAU,IAAI,CAAC,aAAa,mBAAmB,QAAQ,CAAC,CAAC,IACxE,CAAC;AAAA,IACL;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;;;AC1QA,SAAS,cAAc,oBAAoB;;;ACa3C,IAAM,eAAe;AAErB,SAAS,UAAU,KAAsB;AACvC,MAAI;AACF,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,UAAM,IAAI,MAAM,wDAAwD;AAAA,EAC1E;AACF;AAEA,SAAS,SAAS,OAAyC;AACzD,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,GAAG;AACxE,WAAO;AAAA,EACT;AACA,SAAO,CAAC;AACV;AAEA,SAAS,QAAQ,OAAoC;AACnD,SAAO,MAAM,QAAQ,KAAK,IAAK,QAA+B,CAAC;AACjE;AAEA,SAAS,SAAS,OAAwB;AACxC,SAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;AAEA,SAAS,SAAS,OAAoC;AACpD,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,QAAQ;AACvE;AAEA,SAAS,aAAa,OAA6B;AACjD,QAAM,SAAS,SAAS,KAAK;AAC7B,SAAO;AAAA,IACL,MAAM,SAAS,OAAO,MAAM,CAAC;AAAA,IAC7B,MAAM,SAAS,OAAO,MAAM,CAAC;AAAA,IAC7B,MAAM,SAAS,OAAO,MAAM,CAAC;AAAA,EAC/B;AACF;AAEA,SAAS,aAAa,OAAoC;AACxD,QAAM,OAAO,SAAS,SAAS,KAAK,EAAE,MAAM,CAAC;AAC7C,SAAO,KAAK,SAAS,IAAI,OAAO;AAClC;AAEO,SAAS,cAAc,OAA4B;AACxD,QAAM,SAAS,SAAS,KAAK;AAC7B,SAAO;AAAA,IACL,MAAM,SAAS,OAAO,MAAM,CAAC;AAAA,IAC7B,MAAM,SAAS,OAAO,MAAM,CAAC;AAAA,IAC7B,WAAW,SAAS,OAAO,YAAY,CAAC;AAAA,IACxC,WAAW,SAAS,OAAO,YAAY,CAAC;AAAA,IACxC,OAAO,aAAa,OAAO,OAAO,CAAC;AAAA,IACnC,QAAQ,aAAa,OAAO,QAAQ,CAAC;AAAA,IACrC,MAAM,SAAS,OAAO,MAAM,CAAC;AAAA,IAC7B,WAAW,aAAa,OAAO,OAAO,CAAC;AAAA,IACvC,kBAAkB,aAAa,OAAO,cAAc,CAAC;AAAA,EACvD;AACF;AAEA,SAAS,eAAe,OAAqC;AAC3D,QAAM,SAAS,SAAS,KAAK;AAC7B,QAAM,QAAQ,SAAS,OAAO,OAAO,CAAC;AACtC,SAAO;AAAA,IACL,MAAM,SAAS,OAAO,MAAM,CAAC;AAAA,IAC7B,OAAO,SAAS,OAAO,OAAO,CAAC,KAAK;AAAA,IACpC,OAAO,SAAS,OAAO,OAAO,CAAC;AAAA,IAC/B,eAAe,SAAS,OAAO,QAAQ,CAAC;AAAA,IACxC,KAAK,SAAS,MAAM,KAAK,CAAC;AAAA,IAC1B,UAAU,SAAS,MAAM,KAAK,CAAC;AAAA,IAC/B,eAAe,SAAS,OAAO,WAAW,CAAC;AAAA,IAC3C,WAAW,SAAS,MAAM,MAAM,CAAC;AAAA,IACjC,gBAAgB,SAAS,OAAO,YAAY,CAAC;AAAA,EAC/C;AACF;AAEO,SAAS,qBAAqB,OAA8B,SAAyB;AAC1F,QAAM,SAAS,IAAI,gBAAgB;AACnC,uBAAqB,QAAQ,MAAM,KAAK;AACxC,SAAO,IAAI,YAAY,aAAa;AACpC,SAAO,IAAI,YAAY,QAAQ,SAAS,CAAC;AACzC,MAAI,MAAM,UAAU,UAAa,MAAM,MAAM,SAAS,GAAG;AACvD,WAAO,IAAI,SAAS,MAAM,MAAM,KAAK,GAAG,CAAC;AAAA,EAC3C;AACA,MAAI,MAAM,iBAAiB,QAAW;AACpC,WAAO,IAAI,mBAAmB,MAAM,YAAY;AAAA,EAClD;AACA,SAAO,oBAAoB,OAAO,SAAS,CAAC;AAC9C;AAEA,SAAS,qBAAqB,QAAyB,OAA8B;AACnF,MAAI,MAAM,SAAS,OAAO;AACxB,WAAO,IAAI,gBAAgB,MAAM,OAAO;AACxC;AAAA,EACF;AACA,SAAO,IAAI,eAAe,MAAM,SAAS;AAC3C;AAEA,eAAsB,wBAAwB,SAAiB,MAA+B;AAC5F,QAAM,SAAS,IAAI,gBAAgB;AACnC,SAAO,IAAI,SAAS,OAAO;AAC3B,QAAM,OAAO,SAAS,UAAU,MAAM,KAAK,qBAAqB,OAAO,SAAS,CAAC,EAAE,CAAC,CAAC;AACrF,QAAM,QAAQ,SAAS,QAAQ,KAAK,WAAW,CAAC,EAAE,CAAC,CAAC;AACpD,QAAM,OAAO,SAAS,MAAM,MAAM,CAAC;AACnC,MAAI,KAAK,WAAW,GAAG;AACrB,UAAM,IAAI,MAAM,gDAAgD,OAAO,IAAI;AAAA,EAC7E;AACA,SAAO;AACT;AAEA,eAAsB,iBACpB,WACA,SACA,MACiB;AACjB,QAAM,SAAS,IAAI,gBAAgB;AACnC,SAAO,IAAI,SAAS,SAAS;AAC7B,SAAO,IAAI,sBAAsB,OAAO;AACxC,QAAM,OAAO,SAAS,UAAU,MAAM,KAAK,cAAc,OAAO,SAAS,CAAC,EAAE,CAAC,CAAC;AAC9E,QAAM,QAAQ,SAAS,QAAQ,KAAK,WAAW,CAAC,EAAE,CAAC,CAAC;AACpD,QAAM,OAAO,SAAS,MAAM,MAAM,CAAC;AACnC,MAAI,KAAK,WAAW,GAAG;AACrB,UAAM,IAAI,MAAM,yCAAyC,SAAS,IAAI;AAAA,EACxE;AACA,SAAO;AACT;AAEA,SAAS,aAAa,MAAmC;AACvD,QAAM,OAAO,SAAS,SAAS,SAAS,IAAI,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC;AACpE,QAAM,OAAO,KAAK,MAAM;AACxB,MAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAAG;AACjD,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,IAAI;AACxB,WAAO,GAAG,IAAI,QAAQ,GAAG,IAAI,MAAM;AAAA,EACrC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAsB,iBACpB,OACA,MACgC;AAChC,QAAM,QAAQ,KAAK,IAAI,MAAM,OAAO,CAAC;AACrC,QAAM,UAAU,KAAK,IAAI,OAAO,YAAY;AAC5C,QAAM,SAAuB,CAAC;AAC9B,MAAI,OAA2B,qBAAqB,OAAO,OAAO;AAElE,SAAO,SAAS,UAAa,OAAO,SAAS,OAAO;AAClD,UAAM,OAAO,UAAU,MAAM,KAAK,IAAI,CAAC;AACvC,eAAW,YAAY,QAAQ,SAAS,IAAI,EAAE,WAAW,CAAC,GAAG;AAC3D,aAAO,KAAK,cAAc,QAAQ,CAAC;AACnC,UAAI,OAAO,UAAU,OAAO;AAC1B;AAAA,MACF;AAAA,IACF;AACA,WAAO,OAAO,SAAS,QAAQ,aAAa,IAAI,IAAI;AAAA,EACtD;AACA,SAAO;AACT;AAEA,eAAsB,SAAS,SAAiB,MAAmC;AACjF,QAAM,OAAO,SAAS,UAAU,MAAM,KAAK,YAAY,OAAO,EAAE,CAAC,CAAC;AAClE,SAAO;AAAA,IACL,MAAM,SAAS,KAAK,MAAM,CAAC;AAAA,IAC3B,MAAM,SAAS,KAAK,MAAM,CAAC;AAAA,IAC3B,OAAO,SAAS,KAAK,OAAO,CAAC;AAAA,EAC/B;AACF;AAEA,eAAsB,gBAAgB,SAAiB,MAAmC;AACxF,QAAM,OAAO,SAAS,UAAU,MAAM,KAAK,YAAY,OAAO,cAAc,CAAC,CAAC;AAC9E,SAAO;AAAA,IACL,SAAS,KAAK,SAAS,MAAM;AAAA,IAC7B,QAAQ,SAAS,KAAK,QAAQ,CAAC;AAAA,EACjC;AACF;AAEA,eAAsB,qBACpB,SACA,MACyC;AACzC,QAAM,OAAO,SAAS,UAAU,MAAM,KAAK,YAAY,OAAO,sBAAsB,CAAC,CAAC;AACtF,SAAO,QAAQ,KAAK,WAAW,CAAC,EAAE,IAAI,cAAc;AACtD;;;ACtMA,SAAS,gBAAgB;AACzB,SAAS,SAAS,UAAU;AAC5B,SAAS,cAAc;AACvB,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAI1B,IAAM,gBAAgB,UAAU,QAAQ;AASxC,IAAM,iBAAyC;AAAA,EAC7C,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA;AAER;AAEO,SAAS,wBAAwB,WAAuC;AAC7E,SAAO,eAAe,SAAS;AACjC;AAEO,SAAS,mBAAmB,aAAyC;AAC1E,QAAM,aAAa,YAAY,KAAK,EAAE,QAAQ,QAAQ,EAAE,EAAE,YAAY;AACtE,aAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,cAAc,GAAG;AACvD,QAAI,IAAI,YAAY,MAAM,YAAY;AACpC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,sBAA4D;AAChF,QAAM,QAAQ,QAAQ,IAAI,kBAAkB,KAAK;AACjD,QAAM,WAAW,MAAM,SAAS,MAAM,KAAK,MAAM,SAAS,KAAK;AAC/D,QAAM,OAAO,WAAW,SAAS;AACjC,QAAM,OAAO,WAAW,CAAC,OAAO,QAAQ,IAAI,CAAC,QAAQ;AACrD,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,cAAc,MAAM,MAAM;AAAA,MACjD,WAAW,OAAO;AAAA,MAClB,SAAS;AAAA,IACX,CAAC;AACD,WAAO,oBAAoB,MAAM;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,oBAAoB,QAA6C;AAC/E,QAAM,SAAS,oBAAoB,MAAM;AACzC,QAAM,cAAc,OAAO,IAAI,cAAc;AAC7C,QAAM,UAAU,OAAO,IAAI,KAAK;AAChC,QAAM,YAAY,OAAO,IAAI,OAAO;AACpC,MAAI,CAAC,eAAe,CAAC,WAAW,CAAC,WAAW;AAC1C,WAAO;AAAA,EACT;AACA,QAAM,YAAY,mBAAmB,WAAW;AAChD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,EACnC;AACF;AAEA,SAAS,oBAAoB,QAAqC;AAChE,QAAM,SAAS,oBAAI,IAAoB;AACvC,aAAW,QAAQ,OAAO,MAAM,OAAO,GAAG;AACxC,UAAM,MAAM,KAAK,QAAQ,GAAG;AAC5B,QAAI,MAAM,GAAG;AACX;AAAA,IACF;AACA,UAAM,MAAM,KAAK,MAAM,GAAG,GAAG,EAAE,KAAK,EAAE,YAAY;AAClD,UAAM,QAAQ,KAAK,MAAM,MAAM,CAAC,EAAE,KAAK;AACvC,QAAI,OAAO,OAAO;AAChB,aAAO,IAAI,KAAK,KAAK;AAAA,IACvB;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,sBAAsB,KAAK,OAAO;AACxC,IAAM,wBAAwB;AAC9B,IAAM,wBAAwB;AAC9B,IAAM,yBAAyB;AAC/B,IAAM,eAAe;AAWrB,eAAsB,cAAiB,MAAqD;AAC1F,QAAM,YAAY,MAAM,QAAQ,KAAK,OAAO,GAAG,qBAAqB,CAAC;AACrE,MAAI;AACF,WAAO,MAAM,KAAK,EAAE,UAAU,CAAC;AAAA,EACjC,UAAE;AACA,UAAM,GAAG,WAAW,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EACtD;AACF;AAGA,eAAsB,oBAAoB,OAAuB,KAAkC;AACjG,QAAM,aAAa,CAAC,OAAO,MAAM,WAAW,GAAG,KAAK,oCAAoC;AACxF,QAAM,aAAa,CAAC,MAAM,GAAG,KAAK,iDAAiD;AAAA,IACjF,aAAa,MAAM,YAAY;AAAA,IAC/B,aAAa,MAAM,YAAY;AAAA,EACjC,CAAC;AACD,QAAM;AAAA,IACJ,CAAC,UAAU,MAAM,MAAM,SAAS,MAAM,MAAM,SAAS;AAAA,IACrD;AAAA,IACA;AAAA,EACF;AACF;AAGA,eAAsB,UAAU,SAAiB,KAAoC;AACnF,QAAM,SAAS,MAAM;AAAA,IACnB,CAAC,OAAO,SAAS,QAAQ;AAAA,IACzB;AAAA,IACA,uCAAuC,OAAO;AAAA,EAChD;AACA,QAAM,OAAO,OAAO,KAAK;AACzB,MAAI,CAAC,aAAa,KAAK,IAAI,GAAG;AAC5B,UAAM,IAAI;AAAA,MACR,uCAAuC,OAAO;AAAA,IAChD;AAAA,EACF;AACA,SAAO;AACT;AAGA,eAAsB,OAAO,MAAc,KAAoC;AAC7E,SAAO,MAAM,aAAa,CAAC,QAAQ,IAAI,GAAG,KAAK,mCAAmC,IAAI,IAAI;AAC5F;AAEA,eAAsB,aACpB,MACA,KACA,gBACA,eAAiD,CAAC,GACjC;AACjB,QAAM,UAAU,eAAe;AAC/B,QAAM,MAAM,SAAS,IAAI,WAAW,YAAY;AAChD,MAAI;AAEJ,WAAS,UAAU,GAAG,WAAW,uBAAuB,WAAW,GAAG;AACpE,QAAI;AACF,aAAO,MAAM,mBAAmB,QAAQ,KAAK,CAAC,GAAG,QAAQ,YAAY,GAAG,IAAI,GAAG,GAAG;AAAA,IACpF,SAAS,OAAO;AACd,kBAAY;AACZ,UAAI,WAAW,yBAAyB,CAAC,sBAAsB,KAAK,GAAG;AACrE;AAAA,MACF;AACA,YAAM,MAAM,oBAAoB,OAAO,CAAC;AAAA,IAC1C;AAAA,EACF;AAEA,QAAM,SAAS,qBAAqB,SAAS;AAC7C,QAAM,IAAI,MAAM,OAAO,SAAS,IAAI,GAAG,cAAc,IAAI,MAAM,KAAK,gBAAgB;AAAA,IAClF,OAAO;AAAA,EACT,CAAC;AACH;AAEA,SAAS,iBAAkC;AACzC,QAAM,MAAM,QAAQ,IAAI,kBAAkB,KAAK;AAC/C,MAAI,iBAAiB,KAAK,GAAG,GAAG;AAC9B,WAAO,EAAE,KAAK,QAAQ,UAAU,YAAY,CAAC,GAAG,EAAE;AAAA,EACpD;AACA,SAAO,EAAE,KAAK,YAAY,CAAC,EAAE;AAC/B;AAEA,SAAS,SAAS,WAAmB,WAAgE;AACnG,QAAM,MAAyB,EAAE,GAAG,QAAQ,IAAI;AAChD,SAAO,IAAI,WAAW;AACtB,SAAO,IAAI,cAAc;AACzB,MAAI,SAAS,IAAI;AACjB,SAAO,OAAO,KAAK,SAAS;AAC5B,SAAO;AACT;AAEA,eAAe,mBACb,SACA,MACA,KACiB;AACjB,SAAO,MAAM,IAAI,QAAgB,CAAC,gBAAgB,kBAAkB;AAClE;AAAA,MACE;AAAA,MACA,CAAC,GAAG,IAAI;AAAA,MACR,EAAE,KAAK,WAAW,qBAAqB,SAAS,sBAAsB;AAAA,MACtE,CAAC,OAAqB,QAAgB,WAAmB;AACvD,YAAI,UAAU,MAAM;AAClB,wBAAc,OAAO,OAAO,OAAO,EAAE,QAAQ,OAAO,CAAC,CAAC;AACtD;AAAA,QACF;AACA,uBAAe,MAAM;AAAA,MACvB;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,SAAS,sBAAsB,OAAyB;AACtD,QAAM,WAAW,GAAG,gBAAgB,KAAK,CAAC,IAAI,iBAAiB,KAAK,CAAC,GAAG,YAAY;AACpF,MACE,SAAS,SAAS,SAAS,KAC3B,SAAS,SAAS,2BAA2B,KAC7C,SAAS,SAAS,uBAAuB,KACzC,SAAS,SAAS,gBAAgB,KAClC,SAAS,SAAS,WAAW,GAC7B;AACA,WAAO;AAAA,EACT;AACA,SACE,SAAS,SAAS,SAAS,KAC3B,SAAS,SAAS,WAAW,KAC7B,SAAS,SAAS,kBAAkB,KACpC,SAAS,SAAS,yBAAyB,KAC3C,SAAS,SAAS,SAAS,KAC3B,SAAS,SAAS,YAAY,KAC9B,SAAS,SAAS,cAAc;AAEpC;AAEA,SAAS,gBAAgB,OAAwB;AAC/C,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO;AAAA,EACT;AACA,QAAM,SAAU,MAAwC;AACxD,SAAO,OAAO,WAAW,WAAW,SAAS;AAC/C;AAEA,SAAS,iBAAiB,OAAwB;AAChD,SAAO,iBAAiB,QAAQ,MAAM,UAAU;AAClD;AAEA,SAAS,oBAAoB,SAAyB;AACpD,QAAM,SAAS,KAAK,MAAM,KAAK,OAAO,IAAI,sBAAsB;AAChE,SAAO,yBAAyB,UAAU;AAC5C;AAEA,eAAe,MAAM,SAAgC;AACnD,QAAM,IAAI,QAAc,CAAC,mBAAmB;AAC1C,eAAW,gBAAgB,OAAO;AAAA,EACpC,CAAC;AACH;AAEA,SAAS,qBAAqB,OAAwB;AACpD,QAAM,SAAS,gBAAgB,KAAK;AACpC,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO;AAAA,EACT;AACA,QAAM,UAAU,OAAO,WAAW,QAAQ,GAAG,EAAE,KAAK;AACpD,SAAO,QAAQ,SAAS,IAAI,SAAS,QAAQ,MAAM,GAAG,GAAG,CAAC,MAAM;AAClE;;;ACrRA,IAAM,iBAAiB;AAEvB,SAAS,eAAe,KAAa,OAA0B,OAAqB;AAClF,MAAI,MAAM,KAAK,CAAC,SAAS,KAAK,WAAW,CAAC,GAAG;AAC3C,UAAM,IAAI,MAAM,qBAAqB,GAAG,YAAY,KAAK,6BAA6B;AAAA,EACxF;AACF;AAGO,SAAS,cAAc,KAA6B;AACzD,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,QAAQ,WAAW,GAAG;AACxB,UAAM,IAAI,MAAM,2BAA2B,cAAc,GAAG;AAAA,EAC9D;AAEA,QAAM,QAAQ,QAAQ,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;AAE1D,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,UAAU,MAAM,CAAC,KAAK;AAC5B,QAAI,QAAQ,WAAW,GAAG;AACxB,YAAM,IAAI,MAAM,2BAA2B,cAAc,GAAG;AAAA,IAC9D;AACA,WAAO,EAAE,MAAM,WAAW,QAAQ;AAAA,EACpC;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,mBAAe,KAAK,OAAO,kBAAkB;AAC7C,UAAM,CAAC,WAAW,SAAS,SAAS,IAAI;AACxC,WAAO,EAAE,MAAM,aAAa,WAAW,aAAa,IAAI,SAAS,WAAW,IAAI,WAAW,aAAa,GAAG;AAAA,EAC7G;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,mBAAe,KAAK,OAAO,sBAAsB;AACjD,UAAM,CAAC,WAAW,SAAS,WAAW,OAAO,IAAI;AACjD,WAAO;AAAA,MACL,MAAM;AAAA,MACN,WAAW,aAAa;AAAA,MACxB,SAAS,WAAW;AAAA,MACpB,WAAW,aAAa;AAAA,MACxB,SAAS,WAAW;AAAA,IACtB;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,qBAAqB,GAAG,MAAM,cAAc,GAAG;AACjE;AAOA,eAAsB,gBAAgB,KAAwC;AAC5E,QAAM,SAAS,cAAc,GAAG;AAEhC,MAAI,OAAO,SAAS,WAAW;AAC7B,UAAM,UAAU,MAAM,oBAAoB;AAC1C,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA,WAAW,QAAQ,aAAa;AAAA,MAChC,aAAa,QAAQ;AAAA,MACrB,SAAS,QAAQ;AAAA,MACjB,WAAW,QAAQ;AAAA,MACnB,SAAS,OAAO;AAAA,IAClB;AAAA,EACF;AAEA,QAAM,cAAc,wBAAwB,OAAO,SAAS;AAC5D,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI;AAAA,MACR,mBAAmB,OAAO,SAAS;AAAA,IACrC;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,aAAa;AAC/B,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA,WAAW,OAAO;AAAA,MAClB;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,WAAW,OAAO;AAAA,IACpB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,WAAW,OAAO;AAAA,IAClB;AAAA,IACA,SAAS,OAAO;AAAA,IAChB,WAAW,OAAO;AAAA,IAClB,SAAS,OAAO;AAAA,EAClB;AACF;;;ACzHA,IAAM,sBAAsB;AAC5B,IAAM,kBAAkB;AAOxB,IAAM,0BAA0B,KAAK,KAAK;AAE1C,SAAS,qBAAqB,WAAmB,KAAoB;AACnE,QAAM,KAAK,KAAK,MAAM,SAAS;AAC/B,MAAI,OAAO,MAAM,EAAE,GAAG;AACpB,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,IAAI,QAAQ,IAAI;AAC9B,SAAO,SAAS,KAAK,SAAS;AAChC;AAGO,SAAS,iBAAiB,QAA+B,KAAkC;AAChG,SAAO,OACJ,OAAO,CAAC,UAAU,MAAM,SAAS,mBAAmB,EACpD,IAAI,CAAC,WAAW;AAAA,IACf,OAAO,MAAM,MAAM,KAAK,SAAS,IAAI,MAAM,MAAM,OAAO,MAAM,MAAM;AAAA,IACpE,cAAc,MAAM;AAAA,IACpB,cAAc,qBAAqB,MAAM,WAAW,GAAG;AAAA,EACzD,EAAE,EACD,KAAK,CAAC,MAAM,UAAU,MAAM,aAAa,cAAc,KAAK,YAAY,CAAC;AAC9E;AAUO,SAAS,eAAe,OAAkC;AAC/D,QAAM,YAAY,MAAM,OAAO,OAAO,UAAU;AAChD,QAAM,WAAW,iBAAiB,WAAW,MAAM,GAAG;AACtD,QAAM,iBAAiB,UACpB,OAAO,CAAC,UAAU,MAAM,SAAS,eAAe,EAChD,KAAK,CAAC,MAAM,UAAU,MAAM,UAAU,cAAc,KAAK,SAAS,CAAC;AAEtE,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IACf,YAAY,MAAM;AAAA,IAClB,WAAW,MAAM;AAAA,IACjB;AAAA,IACA;AAAA,IACA,oBAAoB,SAAS,OAAO,CAAC,YAAY,QAAQ,YAAY,EAAE;AAAA,EACzE;AACF;;;AJkBA,IAAM,yBAAyB;AAC/B,IAAM,oBAAoB;AAE1B,SAAS,eAAe,MAAwB;AAC9C,SAAO;AAAA,IACL,kBAAkB,CAAC,UAAU,iBAAiB,OAAO,IAAI;AAAA,IACzD,UAAU,CAAC,YAAY,SAAS,SAAS,IAAI;AAAA,IAC7C,iBAAiB,CAAC,YAAY,gBAAgB,SAAS,IAAI;AAAA,IAC3D,sBAAsB,CAAC,YAAY,qBAAqB,SAAS,IAAI;AAAA,IACrE,yBAAyB,CAAC,YAAY,wBAAwB,SAAS,IAAI;AAAA,IAC3E,kBAAkB,CAAC,WAAW,YAAY,iBAAiB,WAAW,SAAS,IAAI;AAAA,EACrF;AACF;AAEA,eAAe,oBACb,UACA,aACA,MACY;AACZ,SAAO,MAAM,cAAc,OAAO,QAAQ;AACxC,UAAM;AAAA,MACJ;AAAA,QACE,aAAa,SAAS;AAAA,QACtB,SAAS,SAAS;AAAA,QAClB,WAAW,SAAS;AAAA,QACpB;AAAA,MACF;AAAA,MACA;AAAA,IACF;AACA,UAAM,SAAS,eAAe,CAAC,SAAS,OAAO,MAAM,GAAG,CAAC;AACzD,WAAO,MAAM,KAAK,EAAE,UAAU,QAAQ,gBAAgB,CAAC,YAAY,UAAU,SAAS,GAAG,EAAE,CAAC;AAAA,EAC9F,CAAC;AACH;AAEO,SAAS,4BAAkD;AAChE,SAAO;AAAA,IACL;AAAA,IACA,cAAc;AAAA,IACd,KAAK,MAAM,oBAAI,KAAK;AAAA,EACtB;AACF;AAEA,eAAe,MAAM,IAAY,QAAoC;AACnE,MAAI;AACF,UAAM,aAAa,IAAI,QAAW,EAAE,OAAO,CAAC;AAAA,EAC9C,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,mBAAmB,UAA4B,SAAsC;AAC5F,MAAI,SAAS,SAAS,OAAO;AAC3B,WAAO;AAAA,EACT;AACA,QAAM,IAAI;AAAA,IACR,OAAO,OAAO;AAAA,EAChB;AACF;AAEA,eAAe,kBAAkB,SAAoD;AACnF,MAAI,QAAQ,SAAS,SAAS,OAAO;AACnC,WAAO,EAAE,MAAM,OAAO,SAAS,MAAM,QAAQ,eAAe,QAAQ,SAAS,OAAO,EAAE;AAAA,EACxF;AACA,QAAM,UAAU,MAAM,QAAQ,OAAO,wBAAwB,QAAQ,SAAS,OAAO;AACrF,SAAO;AAAA,IACL,MAAM;AAAA,IACN,WAAW,MAAM,QAAQ,OAAO,iBAAiB,QAAQ,SAAS,WAAW,OAAO;AAAA,EACtF;AACF;AAGO,IAAM,kBAAN,MAAsB;AAAA,EACV;AAAA,EAEjB,YAAY,OAA6B,0BAA0B,GAAG;AACpE,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,MAAM,YACJ,KACA,aACA,SACgC;AAChC,UAAM,WAAW,MAAM,KAAK,KAAK,gBAAgB,GAAG;AACpD,WAAO,MAAM,KAAK,KAAK,aAAa,UAAU,aAAa,OAAO,YAAY;AAC5E,aAAO,MAAM,QAAQ,OAAO,iBAAiB;AAAA,QAC3C,OAAO,MAAM,kBAAkB,OAAO;AAAA,QACtC,OAAO,QAAQ,MAAM,SAAS,IAAI,QAAQ,QAAQ;AAAA,QAClD,cAAc,KAAK,oBAAoB,QAAQ,KAAK;AAAA,QACpD,OAAO,QAAQ;AAAA,MACjB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,aAAa,KAAa,aAA4B,OAAmC;AAC7F,UAAM,WAAW,mBAAmB,MAAM,KAAK,KAAK,gBAAgB,GAAG,GAAG,YAAY;AACtF,WAAO,MAAM,KAAK,KAAK,aAAa,UAAU,aAAa,OAAO,EAAE,QAAQ,eAAe,MAAM;AAC/F,YAAM,UAAU,MAAM,eAAe,SAAS,OAAO;AACrD,YAAM,CAAC,YAAY,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,QAC7C,OAAO,gBAAgB,OAAO;AAAA,QAC9B,OAAO,iBAAiB;AAAA,UACtB,OAAO,EAAE,MAAM,OAAO,QAAQ;AAAA,UAC9B,OAAO,CAAC,GAAG,eAAe;AAAA,UAC1B,cAAc,uBAAuB,OAAO,KAAK,KAAK,IAAI,CAAC;AAAA,UAC3D,OAAO;AAAA,QACT,CAAC;AAAA,MACH,CAAC;AACD,aAAO,eAAe;AAAA,QACpB,SAAS,SAAS;AAAA,QAClB,YAAY,WAAW;AAAA,QACvB,WAAW,WAAW;AAAA,QACtB;AAAA,QACA,KAAK,KAAK,KAAK,IAAI;AAAA,MACrB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,WACJ,KACA,aACA,SAC6B;AAC7B,UAAM,WAAW,MAAM,KAAK,KAAK,gBAAgB,GAAG;AACpD,WAAO,MAAM,KAAK,KAAK,aAAa,UAAU,aAAa,OAAO,YAAY;AAC5E,YAAM,SAAS,MAAM,QAAQ,OAAO,iBAAiB;AAAA,QACnD,OAAO,MAAM,kBAAkB,OAAO;AAAA,QACtC,OAAO,CAAC,GAAG,iBAAiB;AAAA,QAC5B,cAAc,KAAK,oBAAoB,QAAQ,KAAK;AAAA,QACpD,OAAO,QAAQ;AAAA,MACjB,CAAC;AACD,UAAI,SAAS,SAAS,OAAO;AAC3B,eAAO,iBAAiB,SAAS,SAAS,MAAM;AAAA,MAClD;AACA,aAAO,sBAAsB,GAAG,SAAS,SAAS,IAAI,SAAS,OAAO,IAAI,SAAS,SAAS,IAAI,MAAM;AAAA,IACxG,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,UAAU,KAAa,aAAgD;AAC3E,UAAM,WAAW,mBAAmB,MAAM,KAAK,KAAK,gBAAgB,GAAG,GAAG,QAAQ;AAClF,WAAO,MAAM,KAAK,KAAK,aAAa,UAAU,aAAa,OAAO,EAAE,QAAQ,eAAe,MAAM;AAC/F,YAAM,UAAU,MAAM,eAAe,SAAS,OAAO;AACrD,YAAM,CAAC,KAAK,WAAW,YAAY,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,QAC7D,OAAO,SAAS,OAAO;AAAA,QACvB,OAAO,qBAAqB,OAAO;AAAA,QACnC,OAAO,gBAAgB,OAAO;AAAA,QAC9B,OAAO,iBAAiB,EAAE,OAAO,EAAE,MAAM,OAAO,QAAQ,GAAG,OAAO,QAAW,cAAc,QAAW,OAAO,EAAE,CAAC;AAAA,MAClH,CAAC;AACD,aAAO;AAAA,QACL,SAAS,SAAS;AAAA,QAClB;AAAA,QACA,gBAAgB,IAAI;AAAA,QACpB,YAAY,WAAW;AAAA,QACvB;AAAA,QACA,WAAW,OAAO,CAAC;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,YACJ,KACA,aACA,SACA,SACA,QACe;AACf,UAAM,WAAW,MAAM,KAAK,KAAK,gBAAgB,GAAG;AACpD,UAAM,KAAK,KAAK,aAAa,UAAU,aAAa,OAAO,YAAY;AACrE,YAAM,QAAQ,MAAM,kBAAkB,OAAO;AAC7C,YAAM,OAAO,oBAAI,IAAY;AAC7B,UAAI,SAAS,uBAAuB,QAAQ,UAAU,KAAK,KAAK,IAAI,CAAC;AAErE,aAAO,CAAC,OAAO,SAAS;AACtB,cAAM,SAAS,MAAM,QAAQ,OAAO,iBAAiB;AAAA,UACnD;AAAA,UACA,OAAO,QAAQ,MAAM,SAAS,IAAI,QAAQ,QAAQ;AAAA,UAClD,cAAc;AAAA,UACd,OAAO;AAAA,QACT,CAAC;AAED,cAAM,QAAsB,CAAC;AAC7B,mBAAW,SAAS,QAAQ;AAC1B,cAAI,CAAC,KAAK,IAAI,MAAM,IAAI,GAAG;AACzB,iBAAK,IAAI,MAAM,IAAI;AACnB,kBAAM,KAAK,KAAK;AAAA,UAClB;AAAA,QACF;AACA,mBAAW,SAAS,CAAC,GAAG,KAAK,EAAE,QAAQ,GAAG;AACxC,kBAAQ,KAAK;AAAA,QACf;AAEA,cAAM,SAAS,OAAO,CAAC;AACvB,YAAI,WAAW,UAAa,OAAO,UAAU,SAAS,GAAG;AACvD,mBAAS,OAAO;AAAA,QAClB;AACA,cAAM,MAAM,QAAQ,YAAY,MAAM;AAAA,MACxC;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,oBAAoB,OAA+C;AACzE,WAAO,UAAU,SAAY,SAAY,uBAAuB,OAAO,KAAK,KAAK,IAAI,CAAC;AAAA,EACxF;AACF;;;AJhQA,IAAM,sBAAsB;AAC5B,IAAM,sBAAsB;AAC5B,IAAM,oBAAoB;AAC1B,IAAM,4BAA4B;AAClC,IAAM,yBAAyB;AAC/B,IAAM,wBAAwB;AA6B9B,SAAS,kBAAkB,OAA2B,SAAiB,YAA4B;AACjG,QAAM,SAAS,OAAO,KAAK;AAC3B,MAAI,WAAW,UAAa,OAAO,SAAS,GAAG;AAC7C,WAAO;AAAA,EACT;AACA,QAAM,UAAUC,SAAQ,IAAI,OAAO,GAAG,KAAK;AAC3C,MAAI,YAAY,UAAa,QAAQ,SAAS,GAAG;AAC/C,WAAO;AAAA,EACT;AACA,QAAM,IAAI,MAAM,mCAAmC,UAAU,eAAe,OAAO,wBAAwB;AAC7G;AAEA,SAAS,iBAAiB,OAAmC;AAC3D,SAAO;AAAA,IACL,OAAO,kBAAkB,MAAM,OAAO,aAAa,OAAO;AAAA,IAC1D,UAAU,kBAAkB,MAAM,UAAU,gBAAgB,UAAU;AAAA,EACxE;AACF;AAEA,SAAS,qBAAqB,OAAe,YAA4B;AACvE,QAAM,SAAS,OAAO,SAAS,OAAO,EAAE;AACxC,MAAI,CAAC,OAAO,UAAU,MAAM,KAAK,UAAU,GAAG;AAC5C,UAAM,IAAI,MAAM,GAAG,UAAU,8BAA8B;AAAA,EAC7D;AACA,SAAO;AACT;AAEA,SAAS,SAAS,UAA0B;AAC1C,UAAQ,SAAS,MAAM,GAAG,EAAE,GAAG,EAAE,KAAK,UAAU,KAAK;AACvD;AAEA,SAAS,gBAAgB,UAA2B;AAClD,SAAO,cAAc,QAAQ,EAAE,SAAS;AAC1C;AAEA,SAAS,WAAW,UAA0B;AAC5C,QAAM,SAAS,cAAc,QAAQ;AACrC,SAAO,OAAO,SAAS,cAAc,GAAG,OAAO,SAAS,IAAI,OAAO,OAAO,IAAI,OAAO,SAAS,KAAK;AACrG;AAEA,SAAS,wBAAwB,UAA0B;AAGzD,SAAO;AACT;AAEA,SAAS,SAAS,MAAoB;AACpC,EAAAA,SAAQ,OAAO,MAAM,KAAK,SAAS,IAAI,IAAI,OAAO,GAAG,IAAI;AAAA,CAAI;AAC/D;AAEA,SAAS,UAAU,OAAsB;AACvC,EAAAA,SAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,CAAI;AAC5D;AAEA,SAAS,cAAc,OAAsB;AAC3C,EAAAA,SAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA,CAAI;AACnD;AAEA,eAAe,UAAU,UAAkB,OAAmC;AAC5E,QAAM,mBAAmB,wBAAwB,QAAQ;AACzD,QAAM,UAAU,IAAI,gBAAgB;AACpC,QAAM,SAAS,MAAM,QAAQ,YAAY,kBAAkB,iBAAiB,KAAK,GAAG;AAAA,IAClF,OAAO,MAAM,SAAS;AAAA,IACtB,OAAO,MAAM;AAAA,IACb,OAAO,gBAAgB,MAAM,IAAI;AAAA,EACnC,CAAC;AACD,MAAI,MAAM,SAAS,MAAM;AACvB,cAAU,MAAM;AAChB;AAAA,EACF;AACA,WAAS,gBAAgB,gBAAgB,IACrC,wBAAwB,WAAW,gBAAgB,GAAG,QAAQ,oBAAI,KAAK,CAAC,IACxE,mBAAmB,SAAS,gBAAgB,GAAG,QAAQ,oBAAI,KAAK,CAAC,CAAC;AACxE;AAEA,eAAe,aAAa,UAAkB,OAAsC;AAClF,QAAM,mBAAmB,wBAAwB,QAAQ;AACzD,QAAM,UAAU,IAAI,gBAAgB;AACpC,QAAM,SAAS,MAAM,QAAQ;AAAA,IAC3B;AAAA,IACA,iBAAiB,KAAK;AAAA,IACtB,MAAM,SAAS;AAAA,EACjB;AACA,MAAI,MAAM,SAAS,MAAM;AACvB,cAAU,MAAM;AAChB;AAAA,EACF;AACA,WAAS,sBAAsB,QAAQ,oBAAI,KAAK,CAAC,CAAC;AACpD;AAEA,eAAe,WAAW,UAAkB,OAAoC;AAC9E,QAAM,mBAAmB,wBAAwB,QAAQ;AACzD,QAAM,UAAU,IAAI,gBAAgB;AACpC,QAAM,UAAU,MAAM,QAAQ,WAAW,kBAAkB,iBAAiB,KAAK,GAAG;AAAA,IAClF,OAAO,MAAM,SAAS;AAAA,IACtB,OAAO,MAAM;AAAA,EACf,CAAC;AACD,MAAI,MAAM,SAAS,MAAM;AACvB,cAAU,OAAO;AACjB;AAAA,EACF;AACA,WAAS,kBAAkB,SAAS,oBAAI,KAAK,CAAC,CAAC;AACjD;AAEA,eAAe,UAAU,UAAkB,OAAmC;AAC5E,QAAM,mBAAmB,wBAAwB,QAAQ;AACzD,QAAM,UAAU,IAAI,gBAAgB;AACpC,QAAM,SAAS,MAAM,QAAQ,UAAU,kBAAkB,iBAAiB,KAAK,CAAC;AAChF,MAAI,MAAM,SAAS,MAAM;AACvB,cAAU,MAAM;AAChB;AAAA,EACF;AACA,WAAS,mBAAmB,QAAQ,oBAAI,KAAK,CAAC,CAAC;AACjD;AAEA,SAAS,eAAe,OAAmB,QAAiB,YAA2B;AACrF,MAAI,QAAQ;AACV,kBAAc,KAAK;AACnB;AAAA,EACF;AACA,WAAS,aAAa,qBAAqB,OAAO,oBAAI,KAAK,CAAC,IAAI,gBAAgB,OAAO,oBAAI,KAAK,CAAC,CAAC;AACpG;AAEA,eAAe,SAAS,UAAkB,OAAkC;AAC1E,QAAM,mBAAmB,wBAAwB,QAAQ;AACzD,QAAM,WAAW,MAAM,YAAY;AACnC,MAAI,WAAW,uBAAuB;AACpC,UAAM,IAAI,MAAM,+BAA+B,sBAAsB,SAAS,CAAC,KAAK;AAAA,EACtF;AAEA,QAAM,UAAU,IAAI,gBAAgB;AACpC,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,UAAU,uBAAuB,MAAM;AAC3C,eAAW,MAAM;AAAA,EACnB,CAAC;AACD,QAAM,SAAS,MAAM,SAAS;AAC9B,QAAM,aAAa,gBAAgB,gBAAgB;AAEnD,MAAI;AACF,IAAAA,SAAQ,OAAO,MAAM,YAAY,gBAAgB;AAAA,CAAgD;AACjG,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA,iBAAiB,KAAK;AAAA,MACtB;AAAA,QACE,YAAY;AAAA,QACZ,UAAU,MAAM,YAAY;AAAA,QAC5B,OAAO,gBAAgB,MAAM,IAAI;AAAA,MACnC;AAAA,MACA,CAAC,UAAU;AACT,uBAAe,OAAO,QAAQ,UAAU;AAAA,MAC1C;AAAA,MACA,WAAW;AAAA,IACb;AAAA,EACF,UAAE;AACA,YAAQ;AAAA,EACV;AACF;AAEA,SAAS,uBAAuB,MAA8B;AAC5D,QAAM,UAAU,MAAY;AAC1B,SAAK;AAAA,EACP;AACA,EAAAA,SAAQ,KAAK,UAAU,OAAO;AAC9B,EAAAA,SAAQ,KAAK,WAAW,OAAO;AAC/B,SAAO,MAAM;AACX,IAAAA,SAAQ,IAAI,UAAU,OAAO;AAC7B,IAAAA,SAAQ,IAAI,WAAW,OAAO;AAAA,EAChC;AACF;AAEA,SAAS,iBAAiB,SAA2B;AACnD,SAAO,QACJ,OAAO,mBAAmB,wCAAwC,EAClE,OAAO,sBAAsB,8CAA8C,EAC3E,OAAO,UAAU,wCAAwC,KAAK;AACnE;AAEA,SAAS,qBAA6B;AACpC,MAAI;AACF,UAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;AACnD,UAAM,MAAM,aAAaC,MAAK,MAAM,MAAM,cAAc,GAAG,MAAM;AACjE,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,WAAO,OAAO,OAAO,YAAY,YAAY,OAAO,QAAQ,SAAS,IAAI,OAAO,UAAU;AAAA,EAC5F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,qBAA2B;AAClC,aAAW,UAAU,CAACD,SAAQ,QAAQA,SAAQ,MAAM,GAAG;AACrD,WAAO,GAAG,SAAS,CAAC,UAAuC;AACzD,UAAI,MAAM,SAAS,SAAS;AAC1B,QAAAA,SAAQ,KAAK,CAAC;AAAA,MAChB;AACA,YAAM;AAAA,IACR,CAAC;AAAA,EACH;AACF;AAEA,SAAS,eAAwB;AAC/B,QAAM,UAAU,IAAI,QAAQ;AAC5B,UACG,KAAK,WAAW,EAChB,YAAY,+EAA+E,EAC3F,QAAQ,mBAAmB,GAAG,iBAAiB,qCAAqC;AAEvF;AAAA,IACE,QACG,QAAQ,QAAQ,EAChB,YAAY,4DAA4D,EACxE,SAAS,cAAc,4DAA4D;AAAA,EACxF,EACG;AAAA,IACC;AAAA,IACA;AAAA,IACA,CAAC,UAAkB,qBAAqB,OAAO,SAAS;AAAA,EAC1D,EACC,OAAO,sBAAsB,sDAAsD,EACnF,OAAO,kBAAkB,gEAAgE,EACzF,OAAO,OAAO,UAAkB,UAAsC;AACrE,UAAM,UAAU,UAAU,KAAK;AAAA,EACjC,CAAC;AAEH;AAAA,IACE,QACG,QAAQ,YAAY,EACpB,YAAY,kEAAkE,EAC9E,SAAS,cAAc,yCAAyC;AAAA,EACrE,EACG,OAAO,sBAAsB,mCAAmC,iBAAiB,EACjF,OAAO,OAAO,UAAkB,UAAyC;AACxE,UAAM,aAAa,UAAU,KAAK;AAAA,EACpC,CAAC;AAEH;AAAA,IACE,QACG,QAAQ,SAAS,EACjB,YAAY,iEAAiE,EAC7E,SAAS,cAAc,4DAA4D;AAAA,EACxF,EACG;AAAA,IACC;AAAA,IACA;AAAA,IACA,CAAC,UAAkB,qBAAqB,OAAO,SAAS;AAAA,EAC1D,EACC,OAAO,sBAAsB,uDAAuD,EACpF,OAAO,OAAO,UAAkB,UAAuC;AACtE,UAAM,WAAW,UAAU,KAAK;AAAA,EAClC,CAAC;AAEH;AAAA,IACE,QACG,QAAQ,QAAQ,EAChB,YAAY,uEAAuE,EACnF,SAAS,cAAc,yCAAyC;AAAA,EACrE,EAAE,OAAO,OAAO,UAAkB,UAAsC;AACtE,UAAM,UAAU,UAAU,KAAK;AAAA,EACjC,CAAC;AAED;AAAA,IACE,QACG,QAAQ,OAAO,EACf,YAAY,6DAA6D,EACzE,SAAS,cAAc,4DAA4D;AAAA,EACxF,EACG;AAAA,IACC;AAAA,IACA;AAAA,IACA,CAAC,UAAkB,qBAAqB,OAAO,YAAY;AAAA,EAC7D,EACC,OAAO,yBAAyB,+CAA+C,sBAAsB,EACrG,OAAO,kBAAkB,gEAAgE,EACzF,OAAO,OAAO,UAAkB,UAAqC;AACpE,UAAM,SAAS,UAAU,KAAK;AAAA,EAChC,CAAC;AAEH,SAAO;AACT;AAEA,eAAsB,KAAK,MAAwC;AACjE,QAAM,UAAU,aAAa;AAC7B,QAAM,QAAQ,WAAW,CAAC,GAAG,IAAI,CAAC;AACpC;AAEA,SAAS,eAAwB;AAC/B,QAAM,eAAeA,SAAQ,KAAK,CAAC;AACnC,MAAI,iBAAiB,QAAW;AAC9B,WAAO;AAAA,EACT;AACA,MAAI;AACF,WAAO,aAAa,YAAY,MAAM,cAAc,YAAY,GAAG;AAAA,EACrE,QAAQ;AACN,WAAO,QAAQ,YAAY,MAAM,cAAc,YAAY,GAAG;AAAA,EAChE;AACF;AAEA,eAAe,SAAwB;AACrC,qBAAmB;AACnB,MAAI;AACF,UAAM,KAAKA,SAAQ,IAAI;AAAA,EACzB,SAAS,OAAgB;AACvB,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,IAAAA,SAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAC1C,IAAAA,SAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,IAAI,aAAa,GAAG;AAClB,QAAM,OAAO;AACf;","names":["join","process","process","join"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,8 +10,9 @@ interface CfCredentials {
|
|
|
10
10
|
readonly email: string;
|
|
11
11
|
readonly password: string;
|
|
12
12
|
}
|
|
13
|
-
/** A fully resolved
|
|
14
|
-
interface
|
|
13
|
+
/** A fully resolved application target. */
|
|
14
|
+
interface ResolvedAppSelector {
|
|
15
|
+
readonly kind: "app";
|
|
15
16
|
readonly raw: string;
|
|
16
17
|
readonly regionKey: RegionKey;
|
|
17
18
|
readonly apiEndpoint: string;
|
|
@@ -19,6 +20,16 @@ interface ResolvedSelector {
|
|
|
19
20
|
readonly spaceName: string;
|
|
20
21
|
readonly appName: string;
|
|
21
22
|
}
|
|
23
|
+
/** A fully resolved space target for space-wide audit-event queries. */
|
|
24
|
+
interface ResolvedSpaceSelector {
|
|
25
|
+
readonly kind: "space";
|
|
26
|
+
readonly raw: string;
|
|
27
|
+
readonly regionKey: RegionKey;
|
|
28
|
+
readonly apiEndpoint: string;
|
|
29
|
+
readonly orgName: string;
|
|
30
|
+
readonly spaceName: string;
|
|
31
|
+
}
|
|
32
|
+
type ResolvedSelector = ResolvedAppSelector | ResolvedSpaceSelector;
|
|
22
33
|
interface CfSessionInput {
|
|
23
34
|
readonly apiEndpoint: string;
|
|
24
35
|
readonly orgName: string;
|
|
@@ -104,8 +115,30 @@ interface AppHealth {
|
|
|
104
115
|
readonly instances: readonly ProcessInstanceStat[];
|
|
105
116
|
readonly lastEvent: AuditEvent | undefined;
|
|
106
117
|
}
|
|
107
|
-
interface
|
|
118
|
+
interface AppCrashSummary {
|
|
119
|
+
readonly appName: string;
|
|
120
|
+
readonly crashCount: number;
|
|
121
|
+
readonly lastCrashAt: string | undefined;
|
|
122
|
+
readonly lastCrashReason: string | undefined;
|
|
123
|
+
readonly crashes: readonly CrashRecord[];
|
|
124
|
+
}
|
|
125
|
+
interface SpaceCrashSummary {
|
|
126
|
+
readonly scope: "space";
|
|
127
|
+
readonly selector: string;
|
|
128
|
+
readonly crashCount: number;
|
|
129
|
+
readonly lastCrashAt: string | undefined;
|
|
130
|
+
readonly apps: readonly AppCrashSummary[];
|
|
131
|
+
}
|
|
132
|
+
type CrashReportSummary = CrashSummary | SpaceCrashSummary;
|
|
133
|
+
type AuditEventScope = {
|
|
134
|
+
readonly kind: "app";
|
|
108
135
|
readonly appGuid: string;
|
|
136
|
+
} | {
|
|
137
|
+
readonly kind: "space";
|
|
138
|
+
readonly spaceGuid: string;
|
|
139
|
+
};
|
|
140
|
+
interface FetchAuditEventsInput {
|
|
141
|
+
readonly scope: AuditEventScope;
|
|
109
142
|
readonly types: readonly string[] | undefined;
|
|
110
143
|
readonly createdAfter: string | undefined;
|
|
111
144
|
readonly limit: number;
|
|
@@ -113,8 +146,11 @@ interface FetchAuditEventsInput {
|
|
|
113
146
|
|
|
114
147
|
/** Calls a Cloud Foundry v3 API path and returns the raw response body. */
|
|
115
148
|
type CurlFn = (path: string) => Promise<string>;
|
|
149
|
+
declare function mapAuditEvent(value: unknown): AuditEvent;
|
|
116
150
|
declare function buildAuditEventsPath(input: FetchAuditEventsInput, perPage: number): string;
|
|
117
|
-
|
|
151
|
+
declare function resolveOrganizationGuid(orgName: string, curl: CurlFn): Promise<string>;
|
|
152
|
+
declare function resolveSpaceGuid(spaceName: string, orgGuid: string, curl: CurlFn): Promise<string>;
|
|
153
|
+
/** Fetches audit events for an app or space, following pagination up to `input.limit`. */
|
|
118
154
|
declare function fetchAuditEvents(input: FetchAuditEventsInput, curl: CurlFn): Promise<readonly AuditEvent[]>;
|
|
119
155
|
declare function fetchApp(appGuid: string, curl: CurlFn): Promise<AppSummary>;
|
|
120
156
|
declare function fetchSshEnabled(appGuid: string, curl: CurlFn): Promise<SshEnabled>;
|
|
@@ -158,6 +194,7 @@ declare function durationToCreatedAfter(raw: string, now: Date): string;
|
|
|
158
194
|
declare function parseTypeFilter(raw: string | undefined): readonly string[];
|
|
159
195
|
declare function toCrashRecord(event: AuditEvent): CrashRecord;
|
|
160
196
|
declare function summarizeCrashes(appName: string, events: readonly AuditEvent[]): CrashSummary;
|
|
197
|
+
declare function summarizeSpaceCrashes(selector: string, events: readonly AuditEvent[]): SpaceCrashSummary;
|
|
161
198
|
|
|
162
199
|
declare function describeEventType(type: string): string;
|
|
163
200
|
declare function formatUptime(seconds: number | undefined): string;
|
|
@@ -166,8 +203,10 @@ declare function formatCpu(cpu: number | undefined): string;
|
|
|
166
203
|
declare function formatRelativeTime(timestamp: string, now: Date): string;
|
|
167
204
|
declare function formatEventLine(event: AuditEvent, now: Date): string;
|
|
168
205
|
declare function formatEventsReport(appName: string, events: readonly AuditEvent[], now: Date): string;
|
|
206
|
+
declare function formatSpaceEventLine(event: AuditEvent, now: Date): string;
|
|
207
|
+
declare function formatSpaceEventsReport(selector: string, events: readonly AuditEvent[], now: Date): string;
|
|
169
208
|
declare function formatSshStatusReport(status: SshStatus, now: Date): string;
|
|
170
|
-
declare function formatCrashReport(summary:
|
|
209
|
+
declare function formatCrashReport(summary: CrashReportSummary, now: Date): string;
|
|
171
210
|
declare function formatStatusReport(health: AppHealth, now: Date): string;
|
|
172
211
|
|
|
173
212
|
/** Typed accessor for the Cloud Foundry v3 API used by the runtime. */
|
|
@@ -176,15 +215,18 @@ interface CfClient {
|
|
|
176
215
|
readonly fetchApp: (appGuid: string) => Promise<AppSummary>;
|
|
177
216
|
readonly fetchSshEnabled: (appGuid: string) => Promise<SshEnabled>;
|
|
178
217
|
readonly fetchWebProcessStats: (appGuid: string) => Promise<readonly ProcessInstanceStat[]>;
|
|
218
|
+
readonly resolveOrganizationGuid: (orgName: string) => Promise<string>;
|
|
219
|
+
readonly resolveSpaceGuid: (spaceName: string, orgGuid: string) => Promise<string>;
|
|
179
220
|
}
|
|
180
|
-
interface
|
|
181
|
-
readonly
|
|
221
|
+
interface CfTargetSession {
|
|
222
|
+
readonly selector: ResolvedSelector;
|
|
182
223
|
readonly client: CfClient;
|
|
224
|
+
readonly resolveAppGuid: (appName: string) => Promise<string>;
|
|
183
225
|
}
|
|
184
226
|
/** Injection seam so the orchestration can be unit-tested without a real CF. */
|
|
185
227
|
interface CfEventsDependencies {
|
|
186
228
|
readonly resolveSelector: (raw: string) => Promise<ResolvedSelector>;
|
|
187
|
-
readonly
|
|
229
|
+
readonly withCfTarget: <T>(selector: ResolvedSelector, credentials: CfCredentials, work: (session: CfTargetSession) => Promise<T>) => Promise<T>;
|
|
188
230
|
readonly now: () => Date;
|
|
189
231
|
}
|
|
190
232
|
interface EventQueryOptions {
|
|
@@ -208,7 +250,7 @@ declare class CfEventsRuntime {
|
|
|
208
250
|
constructor(deps?: CfEventsDependencies);
|
|
209
251
|
fetchEvents(raw: string, credentials: CfCredentials, options: EventQueryOptions): Promise<readonly AuditEvent[]>;
|
|
210
252
|
getSshStatus(raw: string, credentials: CfCredentials, since: string): Promise<SshStatus>;
|
|
211
|
-
getCrashes(raw: string, credentials: CfCredentials, options: CrashQueryOptions): Promise<
|
|
253
|
+
getCrashes(raw: string, credentials: CfCredentials, options: CrashQueryOptions): Promise<CrashReportSummary>;
|
|
212
254
|
getStatus(raw: string, credentials: CfCredentials): Promise<AppHealth>;
|
|
213
255
|
watchEvents(raw: string, credentials: CfCredentials, options: WatchOptions, onEvent: (event: AuditEvent) => void, signal: AbortSignal): Promise<void>;
|
|
214
256
|
private resolveCreatedAfter;
|
|
@@ -218,20 +260,26 @@ interface ParsedAppName {
|
|
|
218
260
|
readonly kind: "appName";
|
|
219
261
|
readonly appName: string;
|
|
220
262
|
}
|
|
221
|
-
interface
|
|
222
|
-
readonly kind: "
|
|
263
|
+
interface ParsedAppPath {
|
|
264
|
+
readonly kind: "appPath";
|
|
223
265
|
readonly regionKey: string;
|
|
224
266
|
readonly orgName: string;
|
|
225
267
|
readonly spaceName: string;
|
|
226
268
|
readonly appName: string;
|
|
227
269
|
}
|
|
228
|
-
|
|
270
|
+
interface ParsedSpacePath {
|
|
271
|
+
readonly kind: "spacePath";
|
|
272
|
+
readonly regionKey: string;
|
|
273
|
+
readonly orgName: string;
|
|
274
|
+
readonly spaceName: string;
|
|
275
|
+
}
|
|
276
|
+
type ParsedSelector = ParsedAppName | ParsedAppPath | ParsedSpacePath;
|
|
229
277
|
/** Parses a raw selector string into its structural parts without any I/O. */
|
|
230
278
|
declare function parseSelector(raw: string): ParsedSelector;
|
|
231
279
|
/**
|
|
232
280
|
* Resolves a raw selector.
|
|
233
281
|
* - Bare app name: based strictly on the CURRENT CF target (no global search).
|
|
234
|
-
* -
|
|
282
|
+
* - Explicit paths: use known region-to-api mapping; trust the provided org/space/app.
|
|
235
283
|
*/
|
|
236
284
|
declare function resolveSelector(raw: string): Promise<ResolvedSelector>;
|
|
237
285
|
|
|
@@ -246,4 +294,4 @@ interface SshStatusInput {
|
|
|
246
294
|
}
|
|
247
295
|
declare function buildSshStatus(input: SshStatusInput): SshStatus;
|
|
248
296
|
|
|
249
|
-
export { APP_EVENT_TYPES, type AppEventType, type AppHealth, type AppSummary, type AuditEvent,
|
|
297
|
+
export { APP_EVENT_TYPES, type AppCrashSummary, type AppEventType, type AppHealth, type AppSummary, type AuditEvent, type AuditEventScope, CRASH_EVENT_TYPES, type CfCliContext, type CfClient, type CfCredentials, type CfEntityRef, type CfEventsDependencies, CfEventsRuntime, type CfSessionInput, type CfTargetSession, type CrashQueryOptions, type CrashRecord, type CrashReportSummary, type CrashSummary, type CurlFn, type CurrentCfTarget, type EventQueryOptions, type FetchAuditEventsInput, type ParsedAppName, type ParsedAppPath, type ParsedSelector, type ParsedSpacePath, type ProcessInstanceStat, type RegionKey, type ResolvedAppSelector, type ResolvedSelector, type ResolvedSpaceSelector, SSH_EVENT_TYPES, type SpaceCrashSummary, type SshEnabled, type SshSession, type SshStatus, type SshStatusInput, type WatchOptions, buildAuditEventsPath, buildSshStatus, cfAppGuid, cfCurl, createDefaultDependencies, describeEventType, durationToCreatedAfter, fetchApp, fetchAuditEvents, fetchSshEnabled, fetchWebProcessStats, filterByTypes, formatBytes, formatCpu, formatCrashReport, formatEventLine, formatEventsReport, formatRelativeTime, formatSpaceEventLine, formatSpaceEventsReport, formatSshStatusReport, formatStatusReport, formatUptime, getApiEndpointForRegion, getRegionKeyForApi, inferSshSessions, isCrashEvent, isSshEvent, mapAuditEvent, parseCfTargetOutput, parseDuration, parseSelector, parseTypeFilter, prepareCfCliSession, readCurrentCfTarget, resolveOrganizationGuid, resolveSelector, resolveSpaceGuid, runCfCommand, sortEventsNewestFirst, summarizeCrashes, summarizeSpaceCrashes, toCrashRecord, withCfSession };
|