import { readFileSync, readdirSync, statSync } from "fs"; import { homedir } from "os"; import { join } from "path"; import type { Page } from "@playwright/test"; // Shared helpers for the LIVE smoke specs (real backend + real model). Kept out of the hermetic // suite (separate dir/config); see e2e/README.md. export const BACKEND = "http://127.0.0.1:8765"; function sidecarToken(): string { const state = process.env.COWORKER_STATE_DIR || (process.platform === "win32" ? join(process.env.APPDATA || homedir(), "coworker") : join(homedir(), ".config", "coworker")); try { return readFileSync(join(state, "sidecar-8765.token"), "utf8").trim(); } catch { return ""; } } /** Fetch from the live sidecar with its per-launch authentication token. */ export function backendFetch(path: string, init: RequestInit = {}): Promise { const headers = new Headers(init.headers); const token = sidecarToken(); if (token) headers.set("X-OpenWorker-Token", token); return fetch(`${BACKEND}${path}`, { ...init, headers }); } /** The expanded scratch base if the backend is up and a model is ready — else null (→ skip). */ export async function scratchBaseIfReady(): Promise { try { const res = await backendFetch("/v1/settings"); const s = await res.json(); if (res.ok && s.model_ready) { return String(s.scratch_base || "~/OpenWorker").replace(/^~(?=\/|$)/, homedir()); } } catch { /* backend unreachable */ } return null; } /** Newest `name` file across the per-session scratch dirs (each live session gets its own). */ export function newestFile(scratchBase: string, name: string): string | null { let best: { path: string; mtime: number } | null = null; let dirs: string[]; try { dirs = readdirSync(scratchBase); } catch { return null; } for (const d of dirs) { const f = join(scratchBase, d, name); try { const st = statSync(f); if (!best || st.mtimeMs > best.mtime) best = { path: f, mtime: st.mtimeMs }; } catch { /* not in this session dir */ } } return best?.path ?? null; } /** Open a fresh Cowork session via the split button's persona menu. */ export async function startCoworkSession(page: Page) { await page.goto("/"); await page.getByRole("button", { name: "Choose a persona" }).click(); await page.getByText(/Produce a deliverable/).click(); } /** Switch the composer's permission mode from the default "Ask for approval". */ export async function selectMode(page: Page, label: "Full access" | "Plan" | "Discuss") { await page.getByText("Ask for approval").click(); await page.getByText(label, { exact: true }).click(); } /** Type a task and send it. */ export async function sendTask(page: Page, text: string) { await page.getByPlaceholder(/Ask the coworker/).fill(text); // exact — "Send" is a substring of the Inbox control's "Sending approvals…" title when unattended. await page.getByRole("button", { name: "Send", exact: true }).click(); }