import React, { useEffect, useRef, useState } from "react"; import path from "node:path"; import fs from "fs-extra"; import { Text } from "ink"; import { SearchableList } from "../components/SearchableList"; import { ensureCloudFoundrySessionFromCache, getAppsWithCache, printTarget } from "../../commands/cf.command"; import { parseCloudFoundryEnvironment } from "../../core/cf-env-parser"; import { readCloudFoundryTarget } from "../../core/cf"; import { runCommand } from "../../core/process"; import { readCache, rememberSelectedApp, rememberEnvironmentFileName } from "../../core/cache"; import { resolveRepositoryPath } from "../../core/repository"; import type { InkInteractionService } from "../services/ink-interaction-service"; import type { TCloudFoundryApp } from "../../core/types"; type TScreenProps = { service: InkInteractionService; onDone: (success: boolean) => void; maxVisibleRows?: number }; export function CfTargetScreen(props: { service: InkInteractionService; onDone: (success: boolean) => void }) { const startedRef = useRef(false); useEffect(() => { if (startedRef.current) return; startedRef.current = true; void (async () => { const target = await readCloudFoundryTarget(); printTarget(target, { interaction: props.service, signal: props.service.signal }); props.onDone(true); })(); }, []); return Working…; } export function CfCacheScreen(props: { service: InkInteractionService; onDone: (success: boolean) => void }) { const startedRef = useRef(false); useEffect(() => { if (startedRef.current) return; startedRef.current = true; void (async () => { const cache = await readCache(); const json = JSON.stringify(cache.cloudFoundry, null, 2); props.service.notify({ level: "muted", message: json }); props.onDone(true); })(); }, []); return Working…; } export function CfAppsScreen(props: TScreenProps) { const startedRef = useRef(false); useEffect(() => { if (startedRef.current) return; startedRef.current = true; void (async () => { try { const ctx = { interaction: props.service, signal: props.service.signal }; const target = await ensureCloudFoundrySessionFromCache(ctx); printTarget(target, ctx); const apps = await getAppsWithCache({ startBackgroundRefresh: true }); for (const app of apps) { props.service.notify({ level: "muted", message: [app.name, app.requestedState, app.processes, app.routes].filter(Boolean).join(" | ") }); } props.onDone(true); } catch (error) { props.service.notify({ level: "error", message: error instanceof Error ? error.message : String(error) }); props.onDone(false); } })(); }, []); return Working…; } /** * Native `cf env`: picks an app (own lightweight picker — same reasoning as * CfLogsScreen.tsx, `resolveTargetAndApp`/`resolveAppSelection` call `prompts` * directly), then runs the same non-inherited `cf env ` capture the * traditional handler uses and writes the same JSON/raw file. */ export function CfEnvScreen(props: TScreenProps) { const [apps, setApps] = useState(undefined); const [appName, setAppName] = useState(undefined); const startedRef = useRef(false); useEffect(() => { void getAppsWithCache({ startBackgroundRefresh: true }).then(setApps); }, []); useEffect(() => { if (!appName || startedRef.current) return; startedRef.current = true; void (async () => { try { const repositoryPath = await resolveRepositoryPath(process.cwd()); const cache = await readCache(); const outputFileName = cache.cloudFoundry.envFileNames[0] ?? "default-env.json"; const result = await runCommand("cf", ["env", appName]); if (result.exitCode !== 0) { throw new Error(result.stderr || result.stdout || "cf env failed"); } const outputPath = path.resolve(repositoryPath, outputFileName); const parsedEnvironment = parseCloudFoundryEnvironment(result.stdout); await fs.writeJson(outputPath, parsedEnvironment, { spaces: 2 }); await rememberSelectedApp(appName); await rememberEnvironmentFileName(outputFileName); props.service.notify({ level: "success", message: `Exported clean JSON env to ${outputPath}` }); props.onDone(true); } catch (error) { props.service.notify({ level: "error", message: error instanceof Error ? error.message : String(error) }); props.onDone(false); } })(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [appName]); if (!appName) { if (!apps) return Loading apps…; return ( ({ title: app.name, value: app.name }))} limit={props.maxVisibleRows !== undefined ? Math.max(1, props.maxVisibleRows - 2) : undefined} onSubmit={setAppName} onCancel={() => props.onDone(false)} /> ); } return Working…; }