/** * Schedule card model (PR1) — pure projection of `ScheduleCardTaskInput` (a * minimal task shape mirroring fields from `ScheduledTask`, redeclared here * so the model does NOT depend on `ScheduledTask` / `scheduler.ts` / * `dashboard-ipc-server.ts` runtime modules) into list-card and detail-card * DTOs. * * Allowed imports: * - `import type { ParsedSchedule } from '../types.js'` (type-only, erasable) * - `import { Cron } from 'croner'` (third-party, already a runtime dep — * used purely for time math on cron expressions; no IO is invoked) * - `import type { ... } from './card-model-types.js'` * * Forbidden: scheduler.ts (ONESHOT_GRACE, tick), schedule-store, dashboard-*. */ import type { ParsedSchedule } from '../types.js'; import type { ButtonState, PaginationMeta, PaginationParams, StatusDot } from './card-model-types.js'; /** Minimal task input — self-contained so PR1 model does not pull in `ScheduledTask`. */ export interface ScheduleCardTaskInput { id: string; name: string; prompt?: string; parsed: ParsedSchedule; enabled: boolean; deliver?: ScheduleDelivery; larkAppId?: string; /** Human bot label (e.g. `zkd-claude-bot`). Optional — when present the * global-scope card row prefixes the row with it so the user can tell * which bot owns the schedule. Falls back to a `larkAppId` short suffix * when missing. */ botName?: string; chatId?: string; rootMessageId?: string; scope?: 'thread' | 'chat'; executionPosition?: 'top-level' | 'topic' | 'new-topic'; topicTitle?: string; /** ISO of the next scheduled run (precomputed by caller). */ nextRunAt?: string; /** ISO of the last completed run. */ lastRunAt?: string; lastStatus?: 'ok' | 'error'; /** Repeat counter shape mirrors `ScheduledTask.repeat` (`src/types.ts:212`): * `times === null` ⇒ forever; finite `times` ⇒ auto-removes after N runs. * `completed` counts how many runs have fired. */ repeat?: { times: number | null; completed: number; }; /** Silent fires (no start banner, model decides whether to send). A fresh * topic is materialized lazily by the first botmux send. */ silent?: boolean; } export type ScheduleKind = ParsedSchedule['kind']; export type ScheduleDelivery = 'origin' | 'local' | 'new-topic'; export type ScheduleExecutionPlacement = 'chat' | 'thread' | 'new-topic' | 'local'; export type ScheduleKindChip = ScheduleKind | 'all'; export interface ScheduleFilterQuery extends PaginationParams { /** Case-insensitive substring match against name + prompt. */ search?: string; kind?: ScheduleKindChip; enabledOnly?: boolean; } export interface RowRenderContext { /** Epoch ms — required so relative-time outputs are deterministic. */ nowMs: number; /** IANA timezone for cron next-run math; defaults to the host's local zone (scheduleTimeZone()). */ timezone?: string; /** Cap prompt length in detail DTO; defaults to 200. */ promptTruncateAt?: number; /** How many next-runs to precompute for detail. Defaults to 5. */ nextRunsCount?: number; } /** Per-button availability for the 3 schedule actions. */ export interface ScheduleActionMatrix { runNow: ButtonState; pause: ButtonState; resume: ButtonState; } export interface ScheduleRowDto { id: string; name: string; /** parsed.display passthrough. */ displayExpr: string; kind: ScheduleKind; enabled: boolean; /** Human relative form: 'in Xm' / 'overdue' / '—'. */ nextRunRelative: string; /** Human relative form: 'Xm ago' / '—'. */ lastRunRelative: string; /** Semantic flag — renderer decides whether to draw a glyph. */ errorIndicator: boolean; /** Semantic dot, useful for list-row decoration. */ dot: StatusDot; /** Passthrough of `ScheduledTask.repeat` so the row can render `n/N` or `n/∞`. */ repeat?: { times: number | null; completed: number; }; actions: ScheduleActionMatrix; raw: ScheduleCardTaskInput; } export interface ScheduleDetailDto { id: string; name: string; enabled: boolean; kind: ScheduleKind; displayExpr: string; deliver: ScheduleDelivery; executionPlacement: ScheduleExecutionPlacement; prompt?: string; /** True when prompt was longer than promptTruncateAt and got cut. */ promptTruncated: boolean; chatId?: string; larkAppId?: string; /** Precomputed list of N upcoming runs (ISO strings, strictly increasing). */ nextRuns: string[]; nextRunAt?: string; lastRunAt?: string; lastStatus?: 'ok' | 'error'; errorIndicator: boolean; /** Passthrough of `ScheduledTask.repeat`. */ repeat?: { times: number | null; completed: number; }; actions: ScheduleActionMatrix; raw: ScheduleCardTaskInput; } export interface KindCounts { all: number; once: number; interval: number; cron: number; } export interface ScheduleListPage { rows: ScheduleRowDto[]; meta: PaginationMeta; kindCounts: KindCounts; } /** Compute UI availability for the 3 schedule action buttons (pure). */ export declare function computeButtonAvailability(task: ScheduleCardTaskInput): ScheduleActionMatrix; export declare function normalizeScheduleDelivery(deliver: ScheduleCardTaskInput['deliver']): ScheduleDelivery; /** Resolve the user-facing execution position from the captured session anchor. */ export declare function resolveScheduleExecutionPlacement(task: Pick): ScheduleExecutionPlacement; export declare function computeDeliveryButtonAvailability(task: ScheduleCardTaskInput, target: 'top-level' | 'topic' | 'new-topic'): ButtonState; export declare function nextScheduleExecutionPosition(task: ScheduleCardTaskInput): 'top-level' | 'topic' | 'new-topic'; /** Build a single ScheduleRowDto for list rendering. */ export declare function toScheduleRowDto(task: ScheduleCardTaskInput, ctx: RowRenderContext): ScheduleRowDto; /** Build the detail-card DTO including precomputed next-N runs and prompt truncation. */ export declare function toScheduleDetailDto(task: ScheduleCardTaskInput, ctx: RowRenderContext): ScheduleDetailDto; /** * Compute the next N runs of a task, as ISO strings. Strictly increasing. * * - `once`: one entry if `runAt >= nowMs` AND `lastRunAt` is absent; else []. * - `interval`: minutes>0 required; base = lastRunAt ?? nowMs; aligns to first * future run > nowMs. * - `cron`: uses `croner` with the host's local timezone (or an injected override). * * Pure w.r.t. the clock: nowMs is injected. `timezone` is injected too; when * omitted it falls back to the host's local zone (scheduleTimeZone()). */ export declare function computeNextNRuns(task: ScheduleCardTaskInput, n: number, ctx: { nowMs: number; timezone?: string; }): string[]; /** Tally kinds across the (unfiltered) task pool — used for the kind chip badges. */ export declare function kindCounts(tasks: ReadonlyArray): KindCounts; /** * Filter tasks by search/kind/enabledOnly. Returns a new array (no mutation). * - `search`: case-insensitive substring across `name` and `prompt`. * - `kind`: exact match on `parsed.kind`; 'all' or undefined is no-op. * - `enabledOnly`: when true, drops tasks with `enabled !== true`. */ export declare function filterSchedules(tasks: ReadonlyArray, query: Pick): ScheduleCardTaskInput[]; /** Slice an already-filtered list into a single page. Clamp rules apply. */ export declare function paginateSchedules(items: ReadonlyArray, page: number | undefined, pageSize: number | undefined): { items: T[]; page: number; pageSize: number; total: number; totalPages: number; }; /** End-to-end pipeline: filter → toRow → paginate → counts. Used by the dashboard endpoint. */ export declare function filterAndPaginateSchedules(tasks: ReadonlyArray, query: ScheduleFilterQuery, ctx: RowRenderContext): ScheduleListPage; //# sourceMappingURL=schedule-card-model.d.ts.map