From 7fe8e9e063bcc6918bac1af4ae4855ca72f70957 Mon Sep 17 00:00:00 2001 From: Mike Orozco Date: Tue, 21 Jul 2026 08:06:58 -0500 Subject: [PATCH] chore: migrate Scheduler to the public SDK --- .zdp/plugin.json | 2 +- README.md | 4 +-- bun.lock | 3 ++ package.json | 3 +- sdk/index.ts | 78 ----------------------------------------- src/main.ts | 2 +- src/renderer.tsx | 2 +- tests/release.test.ts | 18 +++++----- tests/scheduler.test.ts | 14 +++++--- tsconfig.json | 2 +- 10 files changed, 30 insertions(+), 98 deletions(-) delete mode 100644 sdk/index.ts diff --git a/.zdp/plugin.json b/.zdp/plugin.json index af248de..fe3cf95 100644 --- a/.zdp/plugin.json +++ b/.zdp/plugin.json @@ -2,7 +2,7 @@ "apiVersion": 1, "id": "scheduler", "name": "Scheduler", - "version": "0.1.3", + "version": "0.1.4", "description": "Create normal ZCode tasks from timezone-aware cron schedules while ZCode is open.", "entrypoints": { "main": "dist/main.cjs", diff --git a/README.md b/README.md index 9e9ff17..d8ca3a5 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Scheduled runs are ordinary persistent ZCode tasks. They appear immediately in t Open **Extensions → Available** in ZCode and install Scheduler. The host verifies the release archive checksum before staging it. New installs and updates take effect on the next ZCode launch. -Manual installation is also supported: download `zcode-scheduler-v0.1.3.zip` from the [latest release](https://github.com/notmike101/zcode-scheduler/releases/latest), extract the `scheduler` folder, and select it from **Extensions → Installed → Install folder**. +Manual installation is also supported: download `zcode-scheduler-v0.1.4.zip` from the [latest release](https://github.com/notmike101/zcode-scheduler/releases/latest), extract the `scheduler` folder, and select it from **Extensions → Installed → Install folder**. ## Scheduling behavior @@ -30,7 +30,7 @@ Manual installation is also supported: download `zcode-scheduler-v0.1.3.zip` fro ```powershell bun install bun run check -bun run release:package -- --tag v0.1.3 +bun run release:package -- --tag v0.1.4 ``` The release command writes a ZIP, its SHA-256 checksum, and `extension-update.json` to `dist/release`. diff --git a/bun.lock b/bun.lock index e4300be..ea41574 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,7 @@ "zod": "^4.1.12", }, "devDependencies": { + "@notmike101/zcode-extension-sdk": "0.3.0", "@types/bun": "^1.3.4", "@types/node": "^24.10.1", "typescript": "^5.9.3", @@ -17,6 +18,8 @@ }, }, "packages": { + "@notmike101/zcode-extension-sdk": ["@notmike101/zcode-extension-sdk@0.3.0", "", {}, "sha512-MBZGF3+UPUQqHmaW8hUmqncG64oujaOawB5gMM0Vwl/4+rPr8pcoCs+fbzYhRXIKWIXZjsUnyE8ZmA6fkyINhQ=="], + "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], "@types/node": ["@types/node@24.13.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q=="], diff --git a/package.json b/package.json index dd9bca4..edaffa9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zcode-scheduler", - "version": "0.1.3", + "version": "0.1.4", "description": "Schedule normal, visible ZCode tasks with timezone-aware cron expressions.", "private": true, "license": "MIT", @@ -24,6 +24,7 @@ "zod": "^4.1.12" }, "devDependencies": { + "@notmike101/zcode-extension-sdk": "0.3.0", "@types/bun": "^1.3.4", "@types/node": "^24.10.1", "typescript": "^5.9.3" diff --git a/sdk/index.ts b/sdk/index.ts deleted file mode 100644 index ea8b217..0000000 --- a/sdk/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** Type-only ZCode Desktop Extensions API v1 contract, pinned to host v0.2.0. */ -export type ExtensionDisposable = {dispose: () => unknown | Promise}; - -export type ExtensionLogger = { - child: (scope: string) => ExtensionLogger; - debug: (message: string, data?: unknown) => Promise; - info: (message: string, data?: unknown) => Promise; - warn: (message: string, data?: unknown) => Promise; - error: (message: string, data?: unknown) => Promise; -}; - -export type ExtensionModelRef = {providerId: string; modelId: string; variant?: string}; - -export type ExtensionTaskSpec = { - workspacePath: string; - prompt: string; - title?: string; - mode: "plan" | "build" | "edit" | "yolo"; - model?: ExtensionModelRef; - thoughtLevel?: string; - toolAllowlist?: string[]; - toolDenylist?: string[]; - timeoutMs?: number; -}; - -export type ExtensionTaskResultStatus = "succeeded" | "failed" | "cancelled" | "timed_out" | "lost" | "needs_attention"; -export type ExtensionTaskResult = {sessionId: string; status: ExtensionTaskResultStatus; error?: string}; -export type ExtensionTaskRunHandle = { - sessionId: string; - completion: Promise; - stop: () => Promise; -}; - -export type ExtensionManifest = { - apiVersion: 1; - id: string; - name: string; - version: string; - description?: string; - entrypoints: {main?: string; renderer?: string}; - engines: {host: string; zcode: string}; - pages: Array<{id: string; title: string}>; -}; - -export type ExtensionContext = { - manifest: ExtensionManifest; - dataDir: string; - logger: ExtensionLogger; - ipc: { - handle: (method: string, handler: (payload: unknown) => unknown | Promise) => ExtensionDisposable; - emit: (event: string, payload?: unknown) => void; - }; - lifecycle: {onResume: (handler: () => void) => ExtensionDisposable}; - zcode: { - readWorkspaceState: (workspacePath: string) => Promise; - tasks: { - run: (spec: ExtensionTaskSpec) => Promise; - ensureVisible: (spec: {sessionId: string; workspacePath: string; title?: string}) => Promise; - }; - }; -}; - -export type ExtensionBridge = { - invoke(method: string, payload?: unknown): Promise; - on(listener: (event: string, payload: unknown) => void): () => void; -}; - -export type RendererExtension = { - id: string; - mount(container: HTMLElement, bridge: ExtensionBridge): void | (() => void); -}; - -declare global { - interface Window { - zcodeDesktopPlugins?: ExtensionBridge; - ZDP_REGISTER_PLUGIN_RENDERER?: (extension: RendererExtension) => void; - } -} diff --git a/src/main.ts b/src/main.ts index 11418b2..2dbfc42 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,7 @@ import {randomUUID} from "node:crypto"; import {appendFile, mkdir, readFile, readdir, rename, rm, stat, writeFile} from "node:fs/promises"; import path from "node:path"; import {writeJsonAtomic} from "./atomic.ts"; -import type {ExtensionContext, ExtensionTaskRunHandle} from "../sdk/index.ts"; +import type {ExtensionContext, ExtensionTaskRunHandle} from "@notmike101/zcode-extension-sdk/main"; import {editableJobSchema, jobSchema, runRecordSchema, type EditableJob, type RunRecord, type SchedulerJob} from "./schemas.ts"; import {nextRun, preview, systemTimezone, validateCron, validateTimezone} from "./cron.ts"; diff --git a/src/renderer.tsx b/src/renderer.tsx index 02be2cb..1b2d72b 100644 --- a/src/renderer.tsx +++ b/src/renderer.tsx @@ -1,6 +1,6 @@ import {render} from "preact"; import {useEffect, useMemo, useState} from "preact/hooks"; -import type {ExtensionBridge as ZdpBridge} from "../sdk/index.ts"; +import type {ExtensionBridge as ZdpBridge} from "@notmike101/zcode-extension-sdk/renderer"; import type {RunRecord, SchedulerJob} from "./schemas.ts"; import styles from "./scheduler.css"; diff --git a/tests/release.test.ts b/tests/release.test.ts index 1e71da3..491edaf 100644 --- a/tests/release.test.ts +++ b/tests/release.test.ts @@ -3,20 +3,20 @@ import {assertReleaseVersion, releaseBaseName, resolveReleaseTag} from "../scrip describe("release metadata", () => { test("accepts a matching semantic version tag", () => { - expect(assertReleaseVersion("v0.1.3", "0.1.3", "0.1.3")).toBe("0.1.3"); - expect(releaseBaseName("0.1.3")).toBe("zcode-scheduler-v0.1.3"); + expect(assertReleaseVersion("v0.1.4", "0.1.4", "0.1.4")).toBe("0.1.4"); + expect(releaseBaseName("0.1.4")).toBe("zcode-scheduler-v0.1.4"); }); test("ignores pull-request merge refs when inferring a release tag", () => { - expect(resolveReleaseTag(undefined, undefined, "1/merge", "0.1.3")).toBe("v0.1.3"); - expect(resolveReleaseTag(undefined, "branch", "main", "0.1.3")).toBe("v0.1.3"); - expect(resolveReleaseTag(undefined, "tag", "v0.1.3", "0.1.3")).toBe("v0.1.3"); - expect(resolveReleaseTag("v0.1.4", "tag", "v0.1.3", "0.1.3")).toBe("v0.1.4"); + expect(resolveReleaseTag(undefined, undefined, "1/merge", "0.1.4")).toBe("v0.1.4"); + expect(resolveReleaseTag(undefined, "branch", "main", "0.1.4")).toBe("v0.1.4"); + expect(resolveReleaseTag(undefined, "tag", "v0.1.4", "0.1.4")).toBe("v0.1.4"); + expect(resolveReleaseTag("v0.1.5", "tag", "v0.1.4", "0.1.4")).toBe("v0.1.5"); }); test("rejects malformed or inconsistent versions", () => { - expect(() => assertReleaseVersion("release-0.1.3", "0.1.3", "0.1.3")).toThrow("strict vX.Y.Z"); - expect(() => assertReleaseVersion("v0.1.4", "0.1.3", "0.1.3")).toThrow("package version"); - expect(() => assertReleaseVersion("v0.1.3", "0.1.3", "0.1.2")).toThrow("manifest version"); + expect(() => assertReleaseVersion("release-0.1.4", "0.1.4", "0.1.4")).toThrow("strict vX.Y.Z"); + expect(() => assertReleaseVersion("v0.1.5", "0.1.4", "0.1.4")).toThrow("package version"); + expect(() => assertReleaseVersion("v0.1.4", "0.1.4", "0.1.3")).toThrow("manifest version"); }); }); diff --git a/tests/scheduler.test.ts b/tests/scheduler.test.ts index 283525d..9c864c1 100644 --- a/tests/scheduler.test.ts +++ b/tests/scheduler.test.ts @@ -2,9 +2,15 @@ import {afterEach, describe, expect, test} from "bun:test"; import {mkdir, mkdtemp, readFile, rm, writeFile} from "node:fs/promises"; import {tmpdir} from "node:os"; import path from "node:path"; -import type {ExtensionContext, ExtensionTaskSpec} from "../sdk/index.ts"; +import type {ExtensionContext, ExtensionTaskSpec} from "@notmike101/zcode-extension-sdk/main"; import {SchedulerPlugin} from "../src/main.ts"; +type LegacySchedulerContext = Omit & { + zcode: Pick & { + tasks: Pick; + }; +}; + const roots: string[] = []; const jobId = "6a1d0f7a-8eef-47aa-8638-3f2629ea1d5d"; @@ -118,12 +124,12 @@ function createContext(dataDir: string) { warn: async () => undefined, error: async () => undefined, }; - const context: ExtensionContext = { + const context: LegacySchedulerContext = { manifest: { apiVersion: 1, id: "scheduler", name: "Scheduler", - version: "0.1.3", + version: "0.1.4", entrypoints: {}, engines: {host: ">=0.2.0 <1", zcode: ">=3.3.6"}, pages: [{id: "jobs", title: "Scheduler"}], @@ -146,7 +152,7 @@ function createContext(dataDir: string) { }, }, }; - return {context, handlers, runs, visible, finish: resolveCompletion}; + return {context: context as unknown as ExtensionContext, handlers, runs, visible, finish: resolveCompletion}; } async function eventually(predicate: () => boolean | Promise): Promise { diff --git a/tsconfig.json b/tsconfig.json index 6cbf089..a0b9d38 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,5 +12,5 @@ "jsxImportSource": "preact", "types": ["bun", "node"] }, - "include": ["src/**/*.ts", "src/**/*.tsx", "sdk/**/*.ts", "scripts/**/*.ts", "tests/**/*.ts"] + "include": ["src/**/*.ts", "src/**/*.tsx", "scripts/**/*.ts", "tests/**/*.ts"] }