/** Browser-safe protocol shared by the panel, the server plugin and the companion. No Node imports. */ import type { Job, Run, RunStatus, Schedule } from "../core/types.js"; export type { Job, Run, RunStatus, Schedule }; export const AGENT_REQUEST = "pi-scheduler:agent-request"; export const AGENT_REPLY = "pi-scheduler:agent-reply"; export interface AgentRequestMessage { requestId: string; prompt: string; tools: string[] } export type AgentReplyMessage = | { requestId: string; status: "accepted" } | { requestId: string; status: "completed"; text: string } | { requestId: string; status: "failed"; error: string }; export interface JobView { job: Job; running: string[]; scheduleText: string; bound: boolean; } export interface Overview { host: string; storeRoot: string; now: string; workspacePath: string; jobs: JobView[]; recentRuns: RunSummary[]; } export interface RunSummary { id: string; jobId: string; trigger: Run["trigger"]; attempt: number; status: RunStatus; scheduledFor: string; startedAt: string; endedAt?: string; error?: string; sessionIds: string[]; } export interface JobDetail { view: JobView; runs: RunSummary[]; preview: string[] } export type PeerOperation = | "overview" | "job.get" | "job.upsert" | "job.setEnabled" | "job.reschedule" | "job.delete" | "job.runNow" | "job.misfire" | "run.get" | "run.cancel" | "schedule.preview"; export function summarizeRun(run: Run): RunSummary { return { id: run.id, jobId: run.jobId, trigger: run.trigger, attempt: run.attempt, status: run.status, scheduledFor: run.scheduledFor, startedAt: run.startedAt, ...(run.endedAt ? { endedAt: run.endedAt } : {}), ...(run.error ? { error: run.error } : {}), sessionIds: run.steps.map((s) => s.sessionId).filter((s): s is string => typeof s === "string"), }; } /** Matches tool names against an allowlist that may contain `*` wildcards. */ export function toolAllowed(name: string, allow: readonly string[]): boolean { if (allow.length === 0) return true; return allow.some((pattern) => { if (!pattern.includes("*")) return pattern === name; const re = new RegExp(`^${pattern.split("*").map((p) => p.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*")}$`); return re.test(name); }); }