/** * Orquestación del dashboard — cambio de sesión Pi, draft-from-pattern helpers compartidos, * quoting de argumentos de comando. runWorkflowWithUi vive en lifecycle/run-with-ui; dashboard-open * lo importa desde lifecycle. * * dashboard-open.ts importa switchToPiSession; dashboard-down-editor.ts importa * openWorkflowDashboard (reexportado desde acá) y los tipos Dashboard{CommandSubmitter,Opener}. */ import { existsSync } from "node:fs"; import * as path from "node:path"; import type { ExtensionContext } from "@earendil-works/pi-coding-agent"; import { activeRunCount } from "../lib/active-run-query-deps.js"; import { notify } from "../lib/notify.js"; import type { PiSessionModel } from "../pi-session.js"; import { sessionManagerMetadata } from "../pi-session.js"; export type DashboardCommandSubmitter = (command: string) => void; export type DashboardOpener = (submitCommand?: DashboardCommandSubmitter) => Promise; export interface WorkflowDashboardOpenOptions { submitCommand?: DashboardCommandSubmitter; } type SwitchableSessionContext = ExtensionContext & { switchSession?: ( sessionPath: string, options?: { withSession?: (ctx: { ui: { notify?: (message: string, kind?: "info" | "warning" | "error") => void }; }) => Promise | void; }, ) => Promise<{ cancelled: boolean }>; }; function quoteWorkflowCommandArgument(value: string): string { return JSON.stringify(value); } export function parseWorkflowCommandArgument(value: string): string | undefined { const trimmed = value.trim(); if (!trimmed) return undefined; if (trimmed.startsWith('"')) { try { const parsed = JSON.parse(trimmed); if (typeof parsed === "string") return parsed; } catch { return undefined; } } return trimmed; } export async function switchToPiSession( ctx: ExtensionContext, session: PiSessionModel, options: WorkflowDashboardOpenOptions = {}, ): Promise { const sessionFile = session.sessionFile; if (!sessionFile) { notify(ctx, "No se puede cambiar: la sesión de Pi seleccionada no registró un archivo de sesión.", "warning"); return; } const currentFile = sessionManagerMetadata(ctx).sessionFile; if (currentFile && path.resolve(currentFile) === path.resolve(sessionFile)) { notify(ctx, "Ya estás en la sesión de Pi seleccionada.", "info"); return; } const switchSession = (ctx as SwitchableSessionContext).switchSession; if (typeof switchSession !== "function") { if (options.submitCommand) { options.submitCommand(`/workflow switch-session ${quoteWorkflowCommandArgument(sessionFile)}`); return; } notify( ctx, "No se puede cambiar desde este contexto del dashboard. Abrilo desde el prompt con /workflow sessions.", "warning", ); return; } if (!existsSync(sessionFile)) { notify(ctx, `No se puede cambiar: el archivo de sesión ya no existe: ${sessionFile}`, "warning"); return; } const label = session.sessionName || session.sessionId || path.basename(sessionFile); const activeRuns = activeRunCount(); if (activeRuns > 0) notify( ctx, `Cambiando de sesión de Pi; ${activeRuns} workflow run(s) activos en este Pi se cancelarán.`, "warning", ); const result = await switchSession(sessionFile, { withSession: async (nextCtx) => { nextCtx.ui.notify?.(`Se cambió a la sesión de Pi: ${label}`, "info"); }, }); if (result.cancelled) notify(ctx, "Cambio de sesión cancelado.", "warning"); } export { openWorkflowDashboard } from "./open.js";