Skip to content

feat: Windows Task Scheduler backend for connector schedules (design proposal) #421

Description

Problem

Connector schedule installation is macOS-only: on any other platform installConnectorSchedule saves the cron expression but returns "Schedule saved, but native installation is currently macOS-only." (src/schedules.ts:142-149), so the personal-wiki "self-updating brain" pitch doesn't hold on Windows — the schedule silently never runs. All launchd-specific code is already cleanly isolated behind process.platform !== "darwin" early-returns (schedules.ts:142, 364, 488, 853-892), and the run command is stable and platform-neutral ([node, cli.js, "ingest", "all", "--scheduled", "--print"], schedules.ts:798-805), so a Windows backend slots into existing seams.

Proposed design (feedback wanted before I write code)

Mirror the launchd path with schtasks.exe (ships with every Windows edition; no PowerShell dependency):

  1. Shim script instead of a plist. schtasks /TR has a ~261-char limit, no working-directory field, and no output redirection — so write %USERPROFILE%\.openwiki\ingestion.schedule.cmd that cds to the configured cwd and runs "<node>" "<cli.js>" ingest all --scheduled --print >> "%USERPROFILE%\.openwiki\logs\ingestion.schedule.log" 2>&1, mirroring createLaunchAgentPlist's ProgramArguments + log paths (schedules.ts:786-835).
  2. Install/query/delete mirror the launchd helpers: schtasks /Create /TN "OpenWiki Ingestion" /TR <shim> /SC ... /F inside the platform branch of installConnectorSchedule; schtasks /Query /TNisLaunchAgentLoaded; schtasks /Delete /TN ... /F + unlink shim ↔ unloadLaunchAgent/removeLaunchAgentPlist.
  3. Cron → trigger translation reuses the existing platform-agnostic parsers (parseSimpleCronFields, getSingleCronNumber) and supports exactly the subset parseLaunchdCalendarInterval accepts today: minute+hour → /SC DAILY /ST HH:MM; +weekday → /SC WEEKLY /D; +day-of-month → /SC MONTHLY /D; anything month-pinned keeps the existing "too complex for direct installation" warning (schedules.ts:156-158). The translation is a pure function, fully unit-testable.
  4. Wake-from-sleep stays out of scopeschtasks.exe cannot set WakeToRun (needs task XML or Register-ScheduledTask); keep the existing "Wake setup is currently macOS-only." warning (schedules.ts:368). Can be a follow-up.

Open questions for maintainers

  • Naming: ScheduleInstallResult.launchAgentPath and ConnectorScheduleStatus.launchAgentLoaded/launchAgentPlistExists (schedules.ts:30, 39-41) are launchd-named and surface in the credentials UI and openwiki cron help text. Prefer (a) generalized field names (nativeJobPath, nativeJobInstalled — touches more files) or (b) keeping the launchd names and stretching semantics on Windows? I'd propose (a) as a separate preparatory PR if you agree.
  • Testing: CI has no Windows runner (checks.yml matrix is ubuntu-only), so the schtasks exec paths would ship CI-unexercised. I'd unit-test the cron→trigger translation + shim generation as pure functions (runs everywhere) and verify the exec paths on a real Windows 11 machine with before/after schtasks /Query output in the PR, same as fix: restrict ~/.openwiki ACLs on Windows where chmod is a no-op #367's icacls verification. Acceptable?
  • schtasks vs Register-ScheduledTask (PowerShell): schtasks is dependency-free and language-stable via exit codes; PowerShell unlocks WakeToRun but adds a runtime dependency. I'd start with schtasks.

Happy to implement once the direction is blessed — sizing is roughly 250-400 LOC plus ~100-150 LOC of pure-function tests, one PR (or two, if you want the type-renaming split out first).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions