ข้ามไปยังเนื้อหา
Dee Wan 1.0 ยังอยู่ระหว่างพัฒนาและยังไม่เปิดตัว (pre-MVP) — ดู ขอบเขตผลิตภัณฑ์ 1.0

ปลั๊กอิน Scheduling

นี่คือปลั๊กอินแบบ remote service มันตั้งเวลาให้ version ที่ถูก pin หนึ่งตัวถูกเผยแพร่ หรือให้ version ที่เผยแพร่แล้วและถูก pin หนึ่งตัวถูกยกเลิกการเผยแพร่ มันยังรัน release ที่มีชื่อ: กลุ่มรายการเผยแพร่และยกเลิกการเผยแพร่ข้ามโมเดลและภาษา ซึ่งเริ่มดำเนินการในเวลาที่ตั้งไว้หรือทันที มันดำเนินการเปลี่ยนแปลงแต่ละครั้งผ่าน plugin protocol v1 ของ Dee Wan มันมี Cloudflare Worker ของตัวเอง, ฐานข้อมูล D1 ของตัวเอง, cron trigger ของตัวเอง และ UI สำหรับจัดการของตัวเอง ไม่มีโค้ดส่วนใดของมันรันอยู่ภายใน Dee Wan

อินเทอร์เฟซของปลั๊กอิน Scheduling ยังเป็นภาษาอังกฤษ

id ของ manifest คือ dev.dee-wan.scheduling ซึ่งตั้งไว้เป็น PLUGIN_ID ใน src/app.ts และไม่เปลี่ยนระหว่าง release: id ของปลั๊กอินที่ติดตั้งแล้วเปลี่ยนไม่ได้ และ release ที่มี id ต่างออกไปจะถูกปฏิเสธด้วย manifest_identity_changed fork ที่คุณ deploy เป็นปลั๊กอินอีกตัวต้องมี id แบบ reverse-domain ของตัวเอง

ไดเรกทอรีนี้พร้อมเป็น root ของ repository ของตัวเอง คือ https://github.com/dee-wan-cms/dee-wan-scheduling.git จนกว่าจะย้ายจริง มันอยู่ใน repository ของ Dee Wan ที่ plugins/scheduling ขั้นตอนอยู่ในหัวข้อสุดท้าย “การย้ายไปยัง repository ของตัวเอง”

เอกสาร: การทดสอบปลั๊กอิน ตั้งค่าปลั๊กอินนี้ไว้ข้าง Dee Wan ในเครื่อง จากนั้น Scheduling ตั้งแต่ต้นจนจบ จะพาไล่ดูการติดตั้ง การเผยแพร่ตามกำหนดเวลา และความล้มเหลวแต่ละแบบ ฝั่ง core ดู การเขียนปลั๊กอิน, Protocol v1, ความปลอดภัยของปลั๊กอิน และ การทดสอบปลั๊กอิน

ระบบบันทึกข้อมูลความเป็นเจ้าของกำหนดการที่นำเข้าก่อนส่งคำขอ หากคำตอบสูญหาย การยกเลิกจะอ้างอิง การมอบสิทธิ์เดิม รายการ เวอร์ชันที่บันทึก เวลา และการเปลี่ยนสถานะ พารามิเตอร์ schedule_id ของ cancel-draft ไม่จำเป็นต้องส่ง หากไม่ส่ง ระบบจะยกเลิกคำขอที่ยังไม่ทราบผล บันทึกการยกเลิกที่เก็บไว้ จะป้องกันไม่ให้คำขอที่มาช้าสร้างกำหนดการ กำหนดการที่สร้างสำเร็จแล้วยังคงอยู่เพื่อให้เลิกทำการนำเข้าแยกต่างหากได้

ปลั๊กอิน Manifest Dee Wan protocol
0.1.0 v1 v1
Route ใคร ทำอะไร
GET /dee-wan/manifest.json admin ของ Dee Wan Manifest v1 โดยใช้ origin จาก PUBLIC_ORIGIN
GET /assets/scheduling.css เบราว์เซอร์ stylesheet ของ UI สำหรับจัดการ
POST /admin/activation-codes ผู้ดูแลระบบ (operator) พร้อม Bearer ADMIN_SECRET ออกรหัสใช้ครั้งเดียว มีอายุ 15 นาที body {} ออกรหัส activate {"installation_id":"..."} ออกรหัส rotate สำหรับ installation (การติดตั้งหนึ่งชุด) นั้นเท่านั้น ตอบ 201 พร้อม {"code", "purpose", "installation_id", "expires_at"}: purpose เป็น activate หรือ rotate, installation_id เป็น null สำหรับรหัส activate และ expires_at เป็นมิลลิวินาทีนับจาก epoch
POST /dee-wan/activate core ของ Dee Wan activate สร้าง installation และปฏิเสธ installation ที่มีอยู่แล้ว rotate แทนที่ token แต่ละรหัสใช้ได้ครั้งเดียว
POST /dee-wan/operations/schedule-status core ของ Dee Wan การดำเนินการ schedule-status รับข้อมูลคำขอเรียกใช้ที่รับเฉพาะคีย์ที่กำหนด แลกรหัสด้วย token ของ installation อ่านรายการที่ผูกไว้ แล้วตอบกำหนดการที่รอ รอลองใหม่ และกำลังทำงานของรายการนั้นเป็นแถว อ่านอย่างเดียว คำขอที่มีคีย์อื่นนอกเหนือจากที่กำหนดได้ 400 installation ที่ไม่รู้จักได้ 404 รหัสที่ core ไม่ยอมแลก การดำเนินการอื่น หรือรายการที่อ่านไม่ได้ จะตอบ status: "refused"
POST /dee-wan/operations/schedule-column core ของ Dee Wan คอลัมน์ในรายการเนื้อหา schedule-column รับข้อมูลคำขอเรียกใช้ที่รับเฉพาะคีย์ที่กำหนด แลกรหัสของมัน และตอบบล็อก cells หนึ่งบล็อก คือป้ายสีสำหรับแต่ละรายการที่ระบุซึ่งมีกำหนดการที่รออยู่ รอลองใหม่ หรือกำลังรัน (อันที่ใกล้ที่สุด เช่น Publish 2026-09-20 09:00 โทน warning เมื่อรอลองใหม่ หรือรัน) อ่านเฉพาะกำหนดการของ installation นี้และไม่เขียนอะไร รายการที่ไม่มีกำหนดการที่ยังใช้งานอยู่ไม่มีเซลล์ รหัสที่ core ไม่แลกให้ การดำเนินการอื่น รายการที่ผูกไว้ หรืออินพุตที่ไม่ใช่รายการของรหัสรายการ จะได้คำตอบ status: "refused"
POST /dee-wan/operations/schedule-draft core ของ Dee Wan การดำเนินการ schedule-draft แลกรหัส รับ input แบบปิด { locale, transition, due_at, timezone, version_id } (due_at เป็นนาทีเต็มแบบ UTC, timezone เป็นชื่อ IANA, version_id คือเวอร์ชันฉบับร่างที่บันทึกไว้ให้ตรึง) อ่านรายการที่ผูกไว้ผ่าน delegation_id ของข้อมูลผูก แล้วสร้างกำหนดการเผยแพร่แบบตรึงเวอร์ชันเหมือนฟอร์ม โดยตรึงที่ version_id พอดี โดยเก็บ delegation นั้นและอีเมลของผู้เรียก คำขอเดิมซ้ำจะตอบกำหนดการที่สร้างไว้แล้ว การปฏิเสธตอบ status: "refused" ตามชื่อ: invalid_input, invalid_instant, unknown_timezone, past_time, entry_unreadable, requester_unauthorized, locale_unavailable, transition_not_allowed, nothing_to_pin, version_moved, draft_changed (ฉบับร่างปัจจุบันของรายการไม่ใช่ version_id), already_scheduled, execution_in_progress ไม่แทนที่กำหนดการอื่นที่ยังทำงานอยู่
POST /dee-wan/operations/releases core ของ Dee Wan การดำเนินการระดับไซต์ releases แลกรหัสและอ่านขอบเขตของ delegation_id ของข้อมูลผูก release_id ว่าง: ทุก release เป็นแถวและ releases.json เมื่อมี release_id: รายการของ release นั้นที่ผู้เรียกอ่านได้ พร้อมความพร้อมหรือผลลัพธ์ เป็นแถวและ release.json (บันทึกผลรีลีส) อ่านอย่างเดียว
POST /dee-wan/operations/release-edit core ของ Dee Wan การดำเนินการระดับไซต์ release-edit: action เป็น create (name), rename (release_id, name), add / add_future (release_id, instance_id, kind, locale และ transition ที่ไม่บังคับ) หรือ remove (release_id, instance_id, kind, locale) ตอบ release แบบเดียวกับ releases การปฏิเสธตอบ status: "refused" ตามชื่อ ตามที่อธิบายในส่วน Release ด้านล่าง
POST /dee-wan/operations/release-run core ของ Dee Wan การดำเนินการระดับไซต์ release-run เป็นแบบทำลาย: action เป็น schedule (release_id, due_at เป็นนาทีเต็มแบบ UTC, timezone เป็นชื่อ IANA), cancel, run_now หรือ retry delegation ของผู้เรียกจะกลายเป็น delegation ที่ release ใช้ทำงาน run_now และ retry เริ่มรายการแรก ๆ ทันทีและตอบ release
POST /dee-wan/operations/timeline-preview ระบบอัตโนมัติของ core Dee Wan อ่านแผนรีลีสสะสมสำหรับ release_id ภายใต้ delegation ปัจจุบัน ต้องตั้งเวลา มิฉะนั้นเป็น release_time_required การเปลี่ยนที่ทับกันในเวลาเดียวกันเป็น release_time_ambiguous เรียกตรงผ่าน management API, CLI หรือ MCP ไม่ได้
GET /installations/:id?dee_wan_launch=dwl_... เบราว์เซอร์ จาก Dee Wan แลกรหัส ตั้ง session และ redirect ด้วย 303 ไปยังหน้าเดิมโดยไม่มีรหัส การ launch แบบ manage ที่มี dee_wan_view=calendar หรือ dee_wan_view=releases จะ redirect ไปที่ view นั้นแทน
GET /installations/:id Session การ launch จากเนื้อหาแสดงเนื้อหา ฟอร์มตั้งเวลา และรายการตั้งเวลาของเนื้อหานั้น การ launch แบบ manage แสดง agenda
GET /installations/:id?month=&zone=&model=&lang=&state= Session แบบ manage agenda: หนึ่งเดือนในรูปแบบรายการ จัดกลุ่มตามวัน
GET /installations/:id/calendar?month=&zone=&model=&lang=&state= Session แบบ manage ปฏิทิน: หนึ่งเดือนในรูปแบบตาราง พร้อมจำนวนต่อวัน
GET /installations/:id/calendar/:day?zone=&model=&lang=&state= Session แบบ manage หนึ่งวัน (YYYY-MM-DD): รายการตั้งเวลาของวันนั้นเรียงตามเวลาที่ถึงกำหนด แล้วตาม id
GET /installations/:id/schedules/:scheduleId Session รายการตั้งเวลาหนึ่งรายการ พร้อมตัวอย่างเนื้อหาที่อ่านผ่าน delegation ของ session
POST /installations/:id/schedules Session และ CSRF token สร้างรายการตั้งเวลา
POST /installations/:id/schedules/:scheduleId/reschedule Session และ CSRF token เลื่อนรายการตั้งเวลาที่อยู่ในสถานะ pending หรือ retrying ไปยังเวลาใหม่
POST /installations/:id/schedules/:scheduleId/cancel Session และ CSRF token ยกเลิกรายการตั้งเวลาที่อยู่ในสถานะ pending หรือ retrying
GET /installations/:id/releases Session แบบ manage ทุก release เรียงใหม่สุดก่อน และฟอร์มสร้าง release
POST /installations/:id/releases Session แบบ manage และ CSRF token สร้าง release (name ยาว 1 ถึง 80 ตัวอักษร)
GET /installations/:id/releases/:releaseId Session แบบ manage release หนึ่งรายการ: เวลาที่ทำงาน ความคืบหน้า รายการพร้อมความพร้อม หรือบันทึกผลรีลีส
POST /installations/:id/releases/:releaseId/rename Session แบบ manage และ CSRF token เปลี่ยนชื่อ release
POST /installations/:id/releases/:releaseId/schedule Session แบบ manage และ CSRF token ตั้งหรือเลื่อนเวลา (local, timezone, fold) ของ release ที่เป็น draft หรือ scheduled
POST /installations/:id/releases/:releaseId/run Session แบบ manage และ CSRF token รัน release ที่เป็น draft หรือ scheduled ทันที
POST /installations/:id/releases/:releaseId/cancel Session แบบ manage และ CSRF token ยกเลิกการตั้งเวลาของ release ที่ตั้งเวลาไว้ หรือหยุด release ที่กำลังทำงาน
POST /installations/:id/releases/:releaseId/retry Session แบบ manage และ CSRF token รันรายการที่ล้มเหลวและถูกยกเลิกของ release ที่จบแล้วอีกครั้ง
POST /installations/:id/releases/:releaseId/items/:itemId/remove Session และ CSRF token ลบรายการขณะที่ release ยังไม่เริ่ม session จากเนื้อหาลบได้เฉพาะรายการของ entry ของตนเอง
POST /installations/:id/release-items Session จากเนื้อหาและ CSRF token เพิ่ม entry ของ session ลงใน release (release) หรือ release ใหม่ (new_release) พร้อม transition แบบ kind:to และ locale ที่ไม่บังคับ version_mode: ordinary (ค่าเริ่มต้น) / future (เผยแพร่เท่านั้น)
POST /admin/installations/:id/delete-data ผู้ดูแลระบบ พร้อม Bearer ADMIN_SECRET ลบข้อมูลปลั๊กอินทั้งหมดของ installation นั้น รวมถึง release และตอบ {"deleted": true} ตอบ 404 not_found หากปลั๊กอินไม่มี installation นั้น ตอบ 409 execution_in_progress ขณะที่รายการตั้งเวลาหรือรายการของ release หนึ่งของมันกำลังทำงาน

scheduled() รัน runner ของรายการตั้งเวลาหนึ่ง tick แล้วรัน release หนึ่ง tick

ใช้ Node 24 และเวอร์ชัน npm ที่รีโพซิทอรี Dee Wan กำหนด เริ่มจากรากของรีโพซิทอรี:

ตัวติดตั้งที่อนุมัติของรีโพซิทอรีหลักติดตั้งเครื่องมือที่ใช้ร่วมกัน ให้ build packages/plugin-conformance ก่อนรันปลั๊กอิน เพราะ dependency ในเครื่อง file:../../packages/plugin-conformance ใช้แพ็กเกจที่ build แล้ว ตัวติดตั้งหลักไม่ได้ติดตั้งปลั๊กอินในไดเรกทอรีย่อยนี้แยกต่างหาก หากยังไม่มีลิงก์ไปยังแพ็กเกจทดสอบในเครื่อง ให้สร้างด้วยคำสั่งด้านล่างจากรากของรีโพซิทอรี หากมีลิงก์ที่ถูกต้องอยู่แล้ว ให้ใช้ลิงก์เดิม อย่าเขียนทับไฟล์หรือไดเรกทอรีอื่น หากใช้รีโพซิทอรีปลั๊กอินแยกต่างหาก ให้เปลี่ยน dependency นี้เป็นเวอร์ชันที่เผยแพร่ใน npm แล้วจึงใช้ npm install แต่ขณะนี้ยังไม่มีเวอร์ชันที่เผยแพร่

ลิงก์นี้ชี้ dependency แบบ file: ที่ประกาศไว้ไปยัง packages/plugin-conformance ใน checkout นี้:

หน้าต่างเทอร์มินัล
mkdir -p plugins/scheduling/node_modules/@dee-wan
ln -s ../../../../packages/plugin-conformance plugins/scheduling/node_modules/@dee-wan/plugin-conformance
หน้าต่างเทอร์มินัล
node scripts/install-dependencies.mjs # from the Dee Wan checkout root
npm --prefix packages/plugin-conformance run build
cd plugins/scheduling
npm run typecheck
npm test
npm run dev # wrangler dev on port 8788, with --test-scheduled
  1. Deploy Worker (ดูด้านล่าง) และตั้งค่า PUBLIC_ORIGIN
  2. สร้างรหัส activation:
    หน้าต่างเทอร์มินัล
    curl -X POST -H "Authorization: Bearer $ADMIN_SECRET" https://scheduling.example/admin/activation-codes
  3. ใน Dee Wan ไปที่ การตั้งค่า → ปลั๊กอิน แล้วติดตั้งจาก https://scheduling.example/dee-wan/manifest.json ด้วยรหัสนั้น มอบ capability และโมเดลให้ workflow:locale จำเป็นเฉพาะเมื่อต้องการตั้งเวลาทีละภาษา หากไม่มี ทุกรายการตั้งเวลาจะย้ายทั้ง translation group
  4. หากต้องการ rotate token ให้สร้างรหัสด้วย {"installation_id":"<id>"} แล้วส่งให้ action rotate ใน Dee Wan

manifest ประกาศ ui_extensions สี่รายการ ทั้งหมดเป็นข้อมูล Dee Wan วาดการ์ดแต่ละใบเอง และเปิด Scheduling ผ่าน delegated launch ตามปกติ

  • schedule-card (entry_sidebar) และ page-schedule (page_sidebar): การ์ด Schedule ข้างรายการหรือหน้าที่โมเดลอยู่ใน grant การ์ดนี้เปิด content action schedule
  • calendar-row (settings_row): การ์ด Calendar ในแถว Scheduling ที่ การตั้งค่า → ปลั๊กอิน การ์ดนี้เปิด manage view calendar จึงแทนที่ปุ่ม ปฏิทิน ธรรมดาในตำแหน่งนั้น
  • releases-row (settings_row): การ์ด Releases ในแถวเดียวกัน การ์ดนี้เปิด manage view releases

Scheduling ไม่ได้ประกาศ field editor

การดำเนินการครอบคลุมสถานะกำหนดการ ป้ายในรายการเนื้อหา รีลีส และระบบอัตโนมัติของ core โดยไม่ได้ประกาศนโยบาย

  • schedule-column (scope: "page", audience: "column", cell: "badge", requires: ["content:read"], ผลลัพธ์ cells) Dee Wan เพิ่มคอลัมน์ Scheduled ในรายการเนื้อหาของทุกโมเดลที่ได้รับสิทธิ์ core ระบุแถวของหน้านั้นที่ผู้ใช้อ่านได้ ไม่ใช่ค่าของแถว และการดำเนินการนี้ตอบป้ายสีให้แต่ละรายการที่มีกำหนดการที่ยังใช้งานอยู่ มันไม่สร้างการมอบสิทธิ์ และไม่ถูกแสดงรายการเป็นการดำเนินการทั่วไป
  • schedule-status (audience: "interactive", requires: ["content:read"], ผลลัพธ์ rows, list_action: true) Dee Wan แสดงไว้ข้างรายการของโมเดลที่ได้รับ สิทธิ์ ผู้ใช้เรียกใช้ที่นั่นแล้วเห็นกำหนดการที่รออยู่ของรายการ ในรายการเนื้อหามันยังเป็นรายการ การทำงานของปลั๊กอิน สำหรับแถวที่ผู้ใช้เลือก และรันหนึ่งครั้งต่อแถว เอเจนต์เรียกใช้ผ่าน API การจัดการหรือ MCP
  • schedule-draft (audience: "automation", requires: ["content:read", "workflow:read", "workflow:publish", "workflow:locale"], ผลลัพธ์ rows และ json, เป็นแบบทำลาย) มีเพียงตัววางแผนเนื้อหาของ Dee Wan ที่เรียกใช้ ในนามเจ้าของแผนเพื่อตั้งเวลา draft เวอร์ชันที่บันทึกไว้พอดี (version_id) ถ้าฉบับร่างปัจจุบันของรายการเป็นเวอร์ชันอื่น มันจะปฏิเสธด้วย draft_changed และไม่ตั้งเวลาใด ๆ มันไม่เคยเป็นการดำเนินการของผู้แก้ไข, API การจัดการ, CLI หรือ MCP และเส้นทางเรียกใช้เหล่านั้นปฏิเสธมัน บล็อก schedule.json ในผลลัพธ์คือข้อมูลอ้างอิงที่ Dee Wan เก็บ: { schedule_id, status, locale, transition, due_at, timezone, local_datetime, pinned_version_id } กำหนดการเก็บ delegation_id ของการดำเนินการ Dee Wan จึงอ่านสิทธิ์ปัจจุบันของเจ้าของอีกครั้งตอนทำงาน เหมือนกำหนดการที่สร้างจากฟอร์ม
  • timeline-preview (scope: "site", audience: "automation", requires: ["content:read", "workflow:read", "workflow:release"], ผล json): ตัวอย่างสะสมถึง release_id สำหรับ core เท่านั้น ภายใต้ delegation ปัจจุบัน ไม่เขียนข้อมูล
  • releases, release-edit และ release-run (scope: "site", audience: "interactive", ผลลัพธ์ rows และ json; release-run เป็นแบบทำลายและต้องมี workflow:publish และ workflow:release ด้วย) ทั้งหมดคือหน้า Releases ในรูปการดำเนินการ แอดมิน, API การจัดการ, CLI และ MCP ทั้งในเครื่องและระยะไกลจึงใช้กฎเดียวกัน Dee Wan เสนอการดำเนินการระดับไซต์ให้ผู้ที่มี site:plugins และผูก manage delegation ของผู้เรียกบนโมเดลที่ได้รับสิทธิ์ซึ่งผู้เรียกอ่านได้ Scheduling อ่านขอบเขตของ delegation นั้นทุกครั้งที่เรียก การดำเนินการหนึ่งอย่างต่อการเปลี่ยนแปลงหนึ่งประเภท พร้อม input action ทำให้ manifest อยู่ในขีดจำกัดจำนวนการดำเนินการ

คัดลอก wrangler.example.jsonc ไปเป็น wrangler.jsonc สำหรับการ deploy ให้แทนที่ database_id ที่เป็น placeholder ด้วย id ที่ wrangler d1 create พิมพ์ออกมา การพัฒนาในเครื่องใช้ placeholder เดิมได้

  • Binding: PLUGIN_DB (D1)
  • Cron: * * * * *
  • ตัวแปร: PUBLIC_ORIGIN, ENVIRONMENT, DEE_WAN_DEV_ORIGINS, MAX_ATTEMPTS (5), MAX_LATENESS_MS (900000), LEASE_MS (60000), MAX_RETRY_AFTER_MS (300000)
  • Secret: ADMIN_SECRET (อย่างน้อย 16 ตัวอักษร), TOKEN_KEY (base64 ของ 32 ไบต์แบบสุ่ม, openssl rand -base64 32)
หน้าต่างเทอร์มินัล
npx wrangler d1 create dee-wan-scheduling
npx wrangler d1 migrations apply dee-wan-scheduling --remote # --local for dev
npx wrangler secret put ADMIN_SECRET
npx wrangler secret put TOKEN_KEY
npm run deploy

ให้ apply ไฟล์ใหม่ใน migrations/ ด้วยคำสั่ง migrations apply เดียวกันก่อน deploy release ที่ต้องใช้ไฟล์เหล่านั้น

การทำ TOKEN_KEY หายจะทำให้ token ของ installation ที่เก็บไว้อ่านไม่ได้ รายการตั้งเวลาจะถูกปฏิเสธด้วย token_unreadable และต้อง rotate ทุก installation

การตั้งค่า ทั้งสอง service, ค่าตั้งของแต่ละตัว และคำสั่ง อยู่ใน การทดสอบปลั๊กอิน ไม่ต้องใช้บัญชี Cloudflare

cookie ของ UI สำหรับจัดการใช้ prefix __Host- และ attribute Secure ดังนั้นเบราว์เซอร์ต้องใช้ HTTPS หรือ localhost base URL ของ Dee Wan ที่เป็น HTTP จะถูกยอมรับก็ต่อเมื่อ ENVIRONMENT=development และ origin ของมันอยู่ใน DEE_WAN_DEV_ORIGINS แบบตรงตัวเท่านั้น มิฉะนั้น base URL ต้องใช้ HTTPS และต้องไม่มี credential, ไม่มี query และไม่มี fragment

  • รายการตั้งเวลาสร้างได้จากการ launch เนื้อหาเท่านั้น (action Schedule) ปลั๊กอินอ่านเนื้อหาด้วย delegation และ (kind, to) ที่เลือกต้องอยู่ใน workflow.transitions ที่ได้รับ delegate
  • ภาษา: เมื่อ installation มี workflow:locale และเนื้อหามีมากกว่าหนึ่งภาษา ฟอร์มจะให้เลือก All languages หรือภาษาเดียว เนื้อหาที่มีภาษาเดียวจะไม่มีตัวเลือกนี้ การเลือก All languages จะย้ายทั้ง translation group ภาษาเดียวย้ายเฉพาะภาษานั้น: (kind, to) ที่เลือกต้องอยู่ใน transition ของภาษานั้นใน workflow.locales และคำสั่งจะส่ง locale ภาษาที่ Dee Wan ไม่ได้แสดงไว้จะถูกปฏิเสธด้วย locale_unavailable
  • การ pin: การเผยแพร่ pin current_version_id การยกเลิกการเผยแพร่ pin published_version_id และทั้งสองแบบยัง pin workflow.revision ด้วย การยกเลิกการเผยแพร่ภาษาเดียว pin published_version_id ของภาษานั้นเอง การเผยแพร่ภาษาเดียว pin current_version_id ของกลุ่ม เพราะ version เดียวมีทุกภาษา revision เป็นของกลุ่มเสมอ รายการตั้งเวลาถูกเก็บโดยผูกกับ instance หลัก โดยตั้ง locale สำหรับภาษาเดียว และเว้นว่างสำหรับทั้งกลุ่ม
  • มีรายการตั้งเวลาที่ยังไม่สิ้นสุด (pending|retrying|running) ได้ไม่เกินหนึ่งรายการต่อ installation, รายการเนื้อหา, kind และภาษา รายการของทั้งกลุ่มกับรายการของภาษาเดียวสำหรับรายการเนื้อหาและ kind เดียวกันก็ขัดกันด้วย เพราะกลุ่มรวมภาษานั้นอยู่ รายการตั้งเวลาใหม่จะแทนที่ (supersede) ทุกรายการ live ที่ขัดกันซึ่งอยู่ในสถานะ pending หรือ retrying ภายใน batch เดียว ดังนั้นรายการของทั้งกลุ่มจะแทนที่รายการรายภาษาของ kind นั้น และรายการของภาษาเดียวจะแทนที่รายการของทั้งกลุ่มของ kind นั้น รายการของภาษาต่างกันรออยู่ด้วยกันได้ หากรายการที่ขัดกันกำลัง running การสร้างจะล้มเหลวด้วย execution_in_progress
  • การตั้งเวลาใหม่ (reschedule) จะสร้างแถวใหม่พร้อม revision ใหม่และ idempotency key ใหม่ revision นับต่อ installation, รายการเนื้อหา และ kind รวมทุกภาษา
  • idempotency key คือ sched:<installation_id>:<schedule_id>:r<revision> ค่านี้คงเดิมในทุกการ retry
  • ยกเลิกได้เฉพาะรายการตั้งเวลาที่อยู่ในสถานะ pending หรือ retrying การยกเลิกรายการที่กำลัง running จะคืน execution_in_progress รายการที่เสร็จแล้วยกเลิกไม่ได้ การยกเลิกไม่เคยเรียก Dee Wan และไม่เคยย้อนกลับ transition
  • ในเวลาที่ดำเนินการ core จะตรวจอำนาจของบุคคลนั้น, การ pin และ revision อีกครั้ง
  • การ launch แบบ manage เปิด agenda หรือเปิดปฏิทินเมื่อ Dee Wan ขอ: รายการตั้งเวลาของโมเดลที่ delegation เข้าถึงได้ ซึ่งดูตัวอย่างได้ และเลื่อนเวลาหรือยกเลิกได้เฉพาะรายการที่บุคคลนั้นเปลี่ยนได้ แต่สร้างรายการตั้งเวลาไม่ได้

สถานะ: pending, retrying, running, completed, refused, cancelled, superseded

เปิด Publication dependencies จากหน้าเนื้อหาหรือหน้า Releases ของ Scheduling แล้ว Review publication set จะเริ่มจากเวอร์ชันที่บันทึกไว้ที่เลือก และติดตามความสัมพันธ์ทั่วไป ความสัมพันธ์เฉพาะภาษา ความสัมพันธ์ในคอมโพเนนต์ และลิงก์ shared section ของภาษานั้น แต่ละขั้นอ่านได้ไม่เกิน 8 รายการ จำกัดการตรวจไว้ที่ 500 รายการที่เข้าถึงและ 100 รายการที่มองเห็น ลิงก์ที่อ่านไม่ได้หรือถูกจำกัดจะไม่แสดง ID หรือชื่อ และทำให้การตรวจไม่สมบูรณ์ เลือกเพิ่มรายการที่ทราบและเผยแพร่ได้สูงสุด 25 รายการจากการตรวจที่สมบูรณ์หรือไม่สมบูรณ์ได้ หน้ายืนยันจะแสดงรายการที่เลือกทั้งหมด และระบบตรวจสอบแต่ละเวอร์ชันอีกครั้งก่อนเพิ่ม เวอร์ชันเก่าที่เผยแพร่ไม่ได้แล้วจะไม่ถูกแทนด้วยเวอร์ชันใหม่ หากตรวจสอบความพร้อมไม่ได้ชั่วคราว ให้กด Save and continue review เพื่อตรวจรายการที่เหลือต่อ หรือเปิดหน้าตรวจทานอีกครั้งเพื่อลองตรวจใหม่ก่อนยืนยัน

รีลีสเผยแพร่หรือเลิกเผยแพร่ได้ไม่เกิน 25 รายการด้วยคำสั่ง CMS เดียว รายการมาจากหลายโมเดลและหลายภาษาในเว็บไซต์เดียวกันได้ แต่ละรายการระบุเนื้อหา ภาษาเดียวหรือทั้งกลุ่มคำแปล ชนิด (publish หรือ unpublish) และสถานะปลายทาง

  • เปิดการ์ด Releases ใน การตั้งค่า → ปลั๊กอิน หรือ ชุดเผยแพร่ บนแดชบอร์ด เป็นการ launch แบบ manage ด้วย view: releases ซึ่งต้องอ่านโมเดลที่ได้รับสิทธิ์ได้ ไม่ต้องมี site:plugins ส่วน operation ระดับเว็บไซต์ยังต้องมี site:plugins เพิ่มเนื้อหาด้วย Add to release บนการ์ด Schedule
  • การเปลี่ยนสถานะต้องยังใช้ได้สำหรับบุคคลนั้นผ่าน delegation เลือกหลายภาษาที่ต่างกันของกลุ่มคำแปลเดียวกันได้ แต่ภาษาเดียวกันซ้ำ หรือทั้งกลุ่มทับกับภาษาใดภาษาหนึ่ง จะถูกปฏิเสธด้วย already_in_release จำกัด 25 รายการ (release_full)
  • แต่ละรายการแสดงชื่อ ภาษา โมเดล การเปลี่ยนแปลง และความพร้อมปัจจุบัน รายการที่อ่านไม่ได้แสดงเพียงจำนวน โดยไม่เปิดเผยชื่อหรือเหตุผล ปุ่ม Run now และ Retry unfinished items ปิดเมื่อมีรายการติดขัด หากตรวจไม่ได้ ผลคือไม่ทราบ ไม่ใช่ถูกบล็อก และยังไม่เริ่มทำงาน
  • สถานะคือ draft, scheduled, running, done, failed, unknown และ cancelled โดย Failed หมายถึง CMS ปฏิเสธคำสั่งและไม่มีการเปลี่ยนการเผยแพร่ใดสำเร็จ ส่วน Outcome unknown หมายถึงคำตอบยังยืนยันไม่ได้ว่าคำสั่งสำเร็จหรือไม่ ตัวรันเก็บผลที่ได้รับและไม่เดาว่ารายการใดสำเร็จ
  • เพิ่มหรือลบรายการได้เฉพาะ draft และ scheduled วัน เวลา และเขตเวลาใช้กฎเดียวกับรายการตั้งเวลา รีลีสทำงานแทนผู้ที่ตั้งเวลา เริ่ม หรือสั่งลองใหม่ ระบบหลักตรวจสิทธิ์ปัจจุบันอีกครั้งก่อน commit
  • รายการแบบปกติยังไม่ตรึงเวอร์ชันตอนเพิ่ม ก่อนส่ง ตัวรันเก็บฉบับร่างปกติล่าสุดสำหรับเผยแพร่ (expected_current_version_id) เวอร์ชันที่เผยแพร่อยู่สำหรับเลิกเผยแพร่ (expected_published_version_id) และรีวิชัน workflow ส่วนเวอร์ชันอนาคตแบบอิสระใช้สาขาของตัวเองตามที่อธิบายด้านล่าง ทั้งชุดส่งไปยัง /releases/commit โดยต้องได้รับ workflow:release และสิทธิ์ของแต่ละการเปลี่ยนแปลง
  • ระบบหลักตรวจสถานะสุดท้ายทั้งชุด รวมถึงความสัมพันธ์ภายในรีลีส ส่วนที่ใช้ร่วมกันและหน้าที่ใช้งานส่วนนั้นเผยแพร่พร้อมกันได้โดยไม่ขึ้นกับลำดับรายการ หลายภาษาของกลุ่มคำแปลเดียวกันเปลี่ยนพร้อมกัน หากสิทธิ์ เวอร์ชัน workflow ภาษา ความสัมพันธ์ หรือจำนวนคำสั่ง SQL ไม่ผ่าน จะไม่มีการเปลี่ยนแปลง ระบบไม่แบ่งคำสั่งที่ใหญ่เกินขีดจำกัดเป็นหลาย commit
  • Run now ตรวจทั้งรีลีสแล้วส่งคำสั่งเดียว tick ตรวจรีลีสที่ถึงกำหนดครั้งละหนึ่งชุด หากติดขัดจะยังเป็น scheduled เก็บ blocked_at และบันทึก Blocked attempts โดยไม่เริ่มใหม่เอง ให้แก้หรือนำรายการออกแล้วกด Run now หรือตั้งเวลาใหม่เพื่อล้างเครื่องหมาย
  • หากชุดรายการเปลี่ยนระหว่างตรวจ การเริ่มหรือลองใหม่จะถูกปฏิเสธด้วย release_changed และไม่ส่งคำสั่ง ส่วนการตั้งเวลาจะตรวจชุดใหม่ใน tick ถัดไป
  • การยกเลิกเวลาทำให้กลับเป็น draft การหยุดป้องกันงานที่ยังไม่ส่ง แต่ย้อนคำสั่งแบบอะตอมมิกที่ส่งไปแล้วไม่ได้ ต้องกู้ผลของคำสั่งนั้นให้ได้
  • คีย์ idempotency ของทั้งคำสั่งคือ rel:<release_id>:a<attempt> เมื่อคำตอบสูญหาย เป็นข้อผิดพลาดชั่วคราว หรือเชื่อถือไม่ได้ ตัวรันคงค่าที่ตรึงทั้งหมดและส่งคำสั่งเดิมด้วยคีย์เดิม เมื่อครบจำนวนลองใหม่อัตโนมัติและยังไม่ทราบผล จะแสดง Outcome unknown การลองใหม่ต้องกู้ผลคำสั่งเดิมก่อน เมื่อได้รับการปฏิเสธแน่ชัดจึงเริ่มความพยายามใหม่ด้วยเวอร์ชันล่าสุดหลังตรวจความพร้อม ไม่ส่งการเผยแพร่ที่สำเร็จแล้วเป็นคำสั่งใหม่
  • บันทึกผลเก็บผลรีลีส เวอร์ชันที่เผยแพร่ของแต่ละรายการ รหัสล่าสุด และเวลาจบ operation releases ให้ดาวน์โหลดเป็น release.json การส่งแจ้งเตือนและการทำ redirect อัตโนมัติเกิดหลัง commit และไม่ย้อน commit นั้น

ใน Add to release ตัวเลือก Version → Latest ordinary draft คงพฤติกรรมปกติ สำหรับการเผยแพร่ เลือก Version → Independent future version เพื่อสร้างเวอร์ชันแยกสำหรับรีลีสนี้ ใช้เวอร์ชันอนาคตกับการเลิกเผยแพร่ไม่ได้ (future_requires_publish) operation release-edit ใช้ action: add_future พร้อม kind: publish ส่วน action: add ใช้ฉบับร่างปกติ หน้าของปลั๊กอินยังใช้ป้ายภาษาอังกฤษ

ใน Dee Wan เปิด เวอร์ชันในอนาคต ของรายการ แล้วเลือกเวอร์ชันของรีลีสนั้น แก้ฟิลด์ หรือส่วนและข้อมูลของหน้าที่ประกอบจากส่วนต่าง ๆ แล้วกด บันทึก ประวัติเวอร์ชันในอนาคต แสดงเวอร์ชันที่บันทึกไว้ของสาขานี้ ซึ่งแก้ย้อนหลังไม่ได้ รีลีสอื่นมีสาขาของรายการเดียวกันได้ การแก้ไขหรือเผยแพร่สาขาไม่เปลี่ยนฉบับร่างปกติ เมื่อเริ่มทำงาน core จะตรึงรีวิชันและเวอร์ชันที่เลือก หลังจากนั้นแก้สาขานี้ไม่ได้ การลองใหม่กู้คำสั่งและเวอร์ชันที่บันทึกไว้ ไม่เปลี่ยนไปใช้ฉบับร่างปกติที่ใหม่กว่า

ตั้งวัน เวลา และเขตเวลาของรีลีสก่อน ดูตัวอย่างถึงรุ่นเผยแพร่นี้ มิฉะนั้นระบบปฏิเสธด้วย release_time_required ตัวอย่างเริ่มจากเนื้อหาที่เผยแพร่แล้ว ใช้การเผยแพร่และเลิกเผยแพร่จากรีลีสก่อนหน้า แล้วจึงใช้รีลีสที่เลือก โดยยังตรวจสิทธิ์อ่านปัจจุบันและไม่เปลี่ยนการเผยแพร่หรือแคชสาธารณะ รีลีสที่เวลาเท่ากันเรียงตามรหัสรีลีสที่คงที่ แต่การเปลี่ยนรายการและภาษาเดียวกันในเวลาเดียวกันถูกปฏิเสธด้วย release_time_ambiguous ให้ตั้งเวลาต่างกัน สำหรับหน้าที่ประกอบจากส่วนต่าง ๆ ให้ใช้ตัวอย่างในตัวแก้ไขเวอร์ชันอนาคตของหน้า

core อ่านแผนนี้ผ่าน operation อัตโนมัติ timeline-preview พร้อม release_id ซึ่งอ่านอย่างเดียว ไม่ใช่ operation ที่เรียกตรงผ่าน management API, CLI หรือ MCP ก่อนเผยแพร่ยังตรวจสิทธิ์ที่มอบหมายปัจจุบัน ฟิลด์และงานที่จำเป็น workflow ความสัมพันธ์ และสื่อที่ตรึงไว้ในเวอร์ชันอนาคตนั้น ระบบปฏิเสธการแก้สาขาที่ตรึงไว้หรือการดูตัวอย่างเป้าหมายที่ใช้ไม่ได้ โดยไม่ใช้ฉบับร่างปกติแทน

ปฏิทินการเผยแพร่ของทั้งไซต์ อ่านเฉพาะรายการตั้งเวลาที่ปลั๊กอินนี้เก็บไว้ Dee Wan ไม่เก็บสำเนาและไม่รัน scheduler

  • ใคร: ทุกคนที่อ่านโมเดลที่ให้ Scheduling ไว้ได้อย่างน้อยหนึ่งโมเดล รวมถึงอ่านได้เฉพาะรายการของตัวเอง ปฏิทิน บน แดชบอร์ด ในรายการ เนื้อหา หรือการ์ด Calendar ในแถวของ Scheduling ในส่วน การตั้งค่า → ปลั๊กอิน คือการ launch แบบ manage ที่มี view: calendar ไม่ต้องมี site:plugins จัดการ ใน การตั้งค่า → ปลั๊กอิน คือการ launch แบบ manage ธรรมดา ยังต้องมี site:plugins และเปิดที่ agenda manifest ประกาศ manage_views: ["calendar", "releases"] และ Dee Wan เพิ่ม dee_wan_view=calendar ใน URL ของ launch คำใบ้นี้ไม่ได้ให้สิทธิ์ใดๆ ปลั๊กอินอ่านค่านี้หลังแลกรหัส manage สำเร็จแล้วเท่านั้น การ launch จากเนื้อหาไม่มีปฏิทินหรือ agenda และตอบ 403 ที่นั่น
  • ขอบเขต: ก่อนทุกหน้าและทุกการเปลี่ยนแปลง ปลั๊กอินถาม Dee Wan GET /api/plugin/v1/delegations/<id> ทุกมุมมอง ตัวนับ ตัวกรอง ข้อความแจ้ง และหน้ารายการตั้งเวลา แสดงเฉพาะรายการตั้งเวลาของโมเดลที่อยู่ในคำตอบนั้น รายการอื่นตอบ not_found ผู้ที่เข้าถึงโมเดลใดไม่ได้เลยจะเห็น No content you can see is scheduled here. Scheduling เก็บรายการตั้งเวลาเอง Dee Wan ไม่เก็บสำเนา
  • รายการของตัวเอง: โมเดลที่ระบุ entries: "checked" คือโมเดลที่บุคคลนั้นอ่านหรือแก้ไขได้เฉพาะรายการของตัวเอง (read:own, update:own) สำหรับโมเดลนั้น ปลั๊กอินถาม Dee Wan GET /api/plugin/v1/delegations/<id>/entries ครั้งละไม่เกิน 100 id ของรายการ ในทุกหน้าและทุกการเปลี่ยนแปลง ปลั๊กอินแสดง นับ และเสนอตัวกรองเฉพาะรายการที่ Dee Wan ระบุ และเสนอ Reschedule หรือ Cancel เฉพาะชนิดที่ระบุไว้กับรายการนั้น รายการตั้งเวลาอื่นตอบ not_found ผู้ที่ไม่มีรายการตั้งเวลาของตัวเองจะเห็นปฏิทินว่าง Scheduling ไม่เก็บข้อมูลเจ้าของ มุมมองไล่ตรวจทุกรายการของโมเดลแบบนี้ในช่วงของตนทีละหน้า และถามแต่ละรายการเพียงครั้งเดียว ไม่ว่ารายการนั้นจะมีการตั้งเวลากี่รายการ จำนวน รายการ และตัวกรองทั้งหมดมาจากชุดที่ตรวจแล้วชุดเดียวกันนี้ ช่วงที่มีรายการแบบนี้เกิน 4,000 รายการ (เกินกว่าที่คำขอหนึ่งครั้งบน Workers Free จะถามได้) จะถูกปฏิเสธทั้งช่วง: This range is too large to check with Dee Wan at once. Nothing is shown. Open a single day. ตัวกรองโมเดลและภาษาของโมเดลแบบนี้มาจากรายการที่ตรวจแล้วในช่วงของมุมมองนั้น
  • ถูกนำออก: เมื่อ Dee Wan ตอบคำขอนั้นด้วย 401 plugin_unauthorized (installation ถูก uninstall ถูกปิด หรือ token ถูกเพิกถอน) ทุกหน้าตอบ 200 และแสดง This installation was removed from Dee Wan or turned off there. Its schedules no longer run. ไม่แสดงรายการตั้งเวลา ตัวนับ หรือการกระทำใด ๆ เมื่อไม่มีขอบเขต ปลั๊กอินไม่รู้ว่าผู้ใช้เห็นโมเดลใดได้ จึงไม่แสดงเลย Cancel หรือ Reschedule ที่ส่งมาก็ไม่เปลี่ยนอะไร
  • Dee Wan ใช้งานไม่ได้: ไม่มีคำตอบ ได้ 5xx หรือ 429 rate_limited หน้าจะตอบ 403 พร้อม Dee Wan is not available right now. Nothing changed. Try again in a minute. และไม่แสดงรายการตั้งเวลา
  • ใครเปลี่ยนอะไรได้: Reschedule และ Cancel แสดงเฉพาะเมื่อชนิดของรายการ (publish หรือ unpublish) อยู่ในรายการของโมเดลนั้น Dee Wan ใส่ชนิดนั้นเมื่อ installation มี workflow:publish หรือ workflow:unpublish บุคคลนั้นแก้ไขรายการได้ (รายการใดก็ได้ของโมเดล หรือภายใต้ update:own เฉพาะรายการของตัวเอง) และ workflow ของโมเดลมีเส้นทางชนิดนั้นที่บุคคลนั้นมีสิทธิ์ ผู้ที่ไม่มีสิ่งเหล่านี้ดูได้อย่างเดียวและหน้าจะบอกไว้ Reschedule หรือ Cancel ที่ส่งมาอยู่ดีจะตอบ not_permitted และไม่เปลี่ยนอะไร การเลื่อนเวลายังอ่านเนื้อหาซ้ำตามด้านล่าง
  • มุมมอง: Calendar เป็นตารางรายเดือน เริ่มที่วันจันทร์ พร้อมจำนวนต่อวัน วันที่มีรายการตั้งเวลาลิงก์ไปยังวันนั้น Agenda เป็นเดือนเดียวกันในรูปแบบรายการ จัดกลุ่มตามวัน Day แสดงรายการตั้งเวลาของวันหนึ่งเรียงตามเวลาที่ถึงกำหนด แล้วตาม id
  • ที่อยู่: เดือนและตัวกรองทั้งหมดอยู่ใน query ดังนั้นทุกมุมมองเป็นลิงก์ GET ธรรมดา และฟอร์มตัวกรองทำงานได้โดยไม่ต้องใช้ JavaScript เดือนก่อนหน้าและเดือนถัดไปคงตัวกรองไว้ ลิงก์ระหว่าง Calendar กับ Agenda และลิงก์ของแต่ละวันก็คงไว้เช่นกัน
  • month: YYYY-MM ค่าเริ่มต้นคือเดือนปัจจุบันใน timezone ที่เลือก ค่าที่ใช้ไม่ได้จะกลับไปใช้ค่าเริ่มต้น
  • zone: timezone แบบ IANA ที่ใช้อ่านวัน ค่าเริ่มต้น UTC แต่ละแถวอยู่ในวันท้องถิ่นของเวลาที่ถึงกำหนดใน timezone นั้น ขอบเขตของเดือนและวันคือเที่ยงคืนท้องถิ่นของ timezone นั้น รวมถึงช่วงเปลี่ยนเวลาและในที่ที่เที่ยงคืนถูกข้ามไป timezone ที่ไม่รู้จักจะถูกปฏิเสธโดยระบุชื่อ ด้วย 400 พร้อมลิงก์ไปยังมุมมองเดียวกันใน UTC
  • model: id ของ model รายการมีเฉพาะ model ที่ installation นี้มีรายการตั้งเวลาและ delegation เข้าถึงได้ แสดงด้วย slug ที่บันทึกไว้ตอนสร้างรายการตั้งเวลา
  • lang: ภาษา รายการมีเฉพาะภาษาที่ installation นี้มีรายการตั้งเวลาแบบภาษาเดียวบนโมเดลที่ delegation เข้าถึงได้ รายการตั้งเวลาของทั้งกลุ่มย้ายทุกภาษา จึงตรงกับตัวกรองภาษาทุกค่า
  • state: current (ค่าเริ่มต้น: pending, retrying, running, completed, refused), live (pending, retrying, running), all หรือสถานะเดียว รายการที่ completed และ refused แสดงรหัสและข้อความของผลลัพธ์
  • ขอบเขต: แต่ละมุมมองอ่านหนึ่งเดือนหรือหนึ่งวัน ด้วยช่วงของ due_at ที่ใช้ index schedule_agenda (installation_id, due_at, id) ปฏิทินนับจำนวนต่อวันใน query เดียวนั้น agenda และมุมมองวันแสดงสูงสุด 200 แถว จากนั้นมีข้อความให้กรองให้แคบลง มุมมองไม่เขียนข้อมูลใดๆ
  • ชื่อ: รายการตั้งเวลาแต่ละรายการแสดงชื่อเรื่องของรายการเนื้อหาที่บันทึกไว้ตอนสร้าง หรือ slug หากรายการนั้นไม่มีชื่อเรื่อง รายการตั้งเวลาของภาษาเดียวแสดงชื่อภาษาเป็นภาษาของ UI เมื่อ timezone ที่ใช้ดูต่างจาก timezone ของรายการตั้งเวลาเอง เวลาจะแสดงใน timezone ที่ใช้ดูด้วย
  • ตัวอย่าง: Open ของรายการตั้งเวลาอ่านเนื้อหาจาก Dee Wan ด้วย delegation ของ session แสดงชื่อเรื่อง model ภาษา และสถานะปัจจุบัน และบอกว่า version ที่ pin และ revision ของ workflow ยังตรงอยู่หรือไม่ สำหรับรายการของภาษาเดียว ภาษา สถานะ และการ pin เป็นของภาษานั้นเอง หาก Dee Wan ไม่แสดงภาษานั้นแล้ว ตัวอย่างจะแสดง locale_unavailable หาก Dee Wan ปฏิเสธการอ่าน หน้านั้นจะบอกเหตุผล Back กลับไปยังมุมมองที่เปิดมา
  • เลื่อนเวลา: เวลาท้องถิ่นและ timezone ใหม่สำหรับรายการตั้งเวลาที่อยู่ในสถานะ pending หรือ retrying การ pin, ชนิด, เป้าหมาย และภาษาคงเดิม ปลั๊กอินจะอ่านเนื้อหาใหม่ผ่าน delegation ของ session ก่อน: บุคคลนั้นต้องยังได้รับ transition นั้น สำหรับภาษานั้นหากรายการมีภาษา และ version ที่ pin กับ revision ต้องยังเป็นปัจจุบัน มิฉะนั้นจะปฏิเสธด้วย locale_unavailable, transition_not_allowed, scheduled_target_stale หรือ workflow_conflict และไม่เปลี่ยนอะไร การเลื่อนเวลาจะเพิ่มแถวใหม่ที่มี revision ถัดไปและทำเครื่องหมายแถวเดิมเป็น superseded จึงได้ idempotency key ใหม่ แถวใหม่ใช้ delegation ของบุคคลที่เลื่อนเวลา รายการที่กำลัง running ตอบ execution_in_progress และรายการที่เสร็จแล้วเลื่อนไม่ได้
  • ยกเลิก: เฉพาะรายการที่อยู่ในสถานะ pending หรือ retrying ตามความหมายของการตั้งเวลา ไม่เคยเรียก Dee Wan เมื่อยกเลิกจากมุมมองใด จะกลับไปยังมุมมองนั้นพร้อมข้อความแจ้ง ที่อยู่ return ยอมรับเฉพาะมุมมองของ installation นี้ และ query ของมันจะถูกอ่านใหม่ ไม่ได้ส่งกลับตามที่รับมา

แผง Workflow ของ Dee Wan ลิงก์มาที่นี่ เมื่อการย้าย workflow ครั้งล่าสุดของรายการทำโดยปลั๊กอินนี้ แผงจะแสดง View schedule ซึ่งเปิดหน้าของปลั๊กอินนี้สำหรับรายการนั้นผ่าน content action Schedule

แต่ละรายการตั้งเวลาเก็บ due_at แบบ UTC, timezone ของ IANA, เวลาท้องถิ่นตามนาฬิกา และ fold การเปลี่ยนกฎของ timezone จะไม่เขียน due_at ใหม่ การคำนวณใช้เพียง Intl.DateTimeFormat

อินพุต ผลลัพธ์
2026-07-01T09:00 America/New_York 2026-07-01 13:00 UTC
2026-03-29T02:30 Europe/Berlin ปฏิเสธด้วย nonexistent_local_time (นาฬิกากระโดดจาก 02:00→03:00)
2026-10-25T02:30 Europe/Berlin ไม่มี fold ปฏิเสธด้วย ambiguous_local_time
เหมือนกัน, fold earlier 2026-10-25 00:30 UTC (CEST)
เหมือนกัน, fold later 2026-10-25 01:30 UTC (CET)
2026-07-01T09:00 Mars/Base ปฏิเสธด้วย unknown_timezone
2025-12-31T23:59 UTC ที่ผ่านไปแล้ว ปฏิเสธด้วย past_time

หน้ายืนยันและรายการแสดงเวลาท้องถิ่นพร้อม zone และเวลา UTC เมื่อ zone เป็น UTC เวลาจะแสดงครั้งเดียว

แต่ละรอบ cron รับรายการที่ถึงกำหนดได้สูงสุด 20 แถวโดยสลับ installation การ claim แบบ compare-and-set เปลี่ยนสถานะและ lease token ที่อ่านไว้ เพิ่ม attempt_count และกำหนด token ใหม่พร้อม lease_until = now + LEASE_MS หลังถอดรหัส token ตัวส่งจะตรวจอีกครั้งว่าตนยังถือ lease อยู่ ยังไม่เกินจำนวนครั้งและระยะเวลาสูงสุด และเหลือเวลาพอให้คำขอทำงานจนครบ timeout ผู้ครองเก่าไม่ส่งและไม่จบแถวของผู้ครองใหม่ การกู้คืนอาจส่ง key และ body เดิมซ้ำ timeout ไม่ได้ยืนยันว่าการทำงานระยะไกลหยุดแล้ว idempotency ของ core จำกัดผลที่เกิด ไม่ได้สัญญาว่าจะมี HTTP request ครั้งเดียว

คำตอบของ Dee Wan ผลลัพธ์ของปลั๊กอิน
200 transition_applied / idempotent_replay completed
400 invalid_request refused ไม่ retry
401 plugin_unauthorized refused ไม่ retry
403 capability_refused / requester_unauthorized refused ไม่ retry
404 content_not_found refused ไม่ retry
409 scheduled_target_stale / workflow_conflict / dependent_content / idempotency_key_reused refused ไม่ retry
422 transition_unavailable / transition_refused refused ไม่ retry
มี outcome แต่ status/code/retry ไม่ตรงหรือฟิลด์ผิดรูปแบบ body ไม่มี outcome บน status อื่น redirect refused, untrusted_response
429 rate_limited retrying ใช้ Retry-After ที่ถูกต้องและไม่เกินเพดาน มิฉะนั้นใช้ exponential backoff พร้อม jitter
503 temporary_failure retrying ใช้ Retry-After ที่ถูกต้องและไม่เกินเพดาน มิฉะนั้นใช้ exponential backoff พร้อม jitter
429/500/502/503/504 ที่ไม่มี outcome เครือข่ายล้มเหลวหรือ timeout (10 s) retrying ใช้ Retry-After ภายในเพดานเมื่อมี มิฉะนั้นใช้ exponential backoff พร้อม jitter
เกิน MAX_ATTEMPTS, เกิน due_at + MAX_LATENESS_MS หรือ Retry-After > MAX_RETRY_AFTER_MS refused retry_exhausted ไม่ส่งอะไรอีก

ตรวจ structured outcome ก่อนใช้กฎสำรองตาม status Retry-After รับวินาทีที่เป็นจำนวนเต็มไม่ติดลบหรือวันที่ HTTP รูปแบบมาตรฐาน (IMF-fixdate) วันที่ที่ผ่านมาแล้วหมายถึงศูนย์ ค่าผิดรูปแบบให้ใช้ backoff ค่าเกิน MAX_RETRY_AFTER_MS หรือเกินเพดานจำนวนครั้งหรืออายุจะได้ retry_exhausted ค่า LEASE_MS ต่ำสุดคือ 11 วินาทีซึ่งยาวกว่า timeout คำขอ 10 วินาที หากเวลา lease ไม่พอจะไม่ส่ง ถือว่าสำเร็จเฉพาะ transition_applied หรือ idempotent_replay

retry_exhausted หลัง network ล้มเหลวหมายความว่าไม่ทราบผลลัพธ์ ดังนั้นให้ตรวจเนื้อหาใน Dee Wan รายการตั้งเวลาจะไม่ถูกส่งช้ากว่าขีดจำกัดความล่าช้าเลย รวมถึงตอนรับช่วง lease ของงานที่ขัดข้องด้วย

ความพยายามแต่ละครั้งถูกบันทึกไว้ใน schedule_attempt (attempt, เวลาเริ่ม, เวลาเสร็จ, code, HTTP status) นี่คือบันทึกเชิงปฏิบัติการสำหรับผู้ใช้ ไม่ใช่ telemetry

  • ข้อมูลที่เก็บ: installation (id, site id, base URL ของ Dee Wan และ token ที่เข้ารหัสด้วย AES-GCM) รหัส activation ที่ hash แล้ว, session ที่ hash แล้ว (30 นาที) พร้อม CSRF token, รายการตั้งเวลา และความพยายามแต่ละครั้ง รวมถึง release พร้อมรายการและบันทึกผลรีลีส ไม่มีการเก็บ snapshot ของเนื้อหาและไม่มีการเก็บ session ผู้ใช้ของ Dee Wan
  • รหัส launch และ token ดิบไม่เคยถูก log หรือเก็บในรูปดิบ
  • การถอนการติดตั้งปลั๊กอินใน Dee Wan จะเพิกถอน token ข้อมูลปลั๊กอินยังคงอยู่ รายการตั้งเวลาที่เหลือจะถูกปฏิเสธด้วย plugin_unauthorized เมื่อถึงกำหนด จากนั้นหน้าของปลั๊กอินจะแจ้งว่า installation ถูกนำออกแล้ว และไม่แสดงรายการตั้งเวลา
  • หากต้องการลบข้อมูลปลั๊กอิน ผู้ดูแลระบบเรียก POST /admin/installations/:id/delete-data พร้อม Bearer ADMIN_SECRET การเรียกจะถูกปฏิเสธด้วย 409 execution_in_progress ขณะที่มีรายการตั้งเวลากำลัง running ไม่ได้สร้างกลไกล้างข้อมูลตามระยะเวลาเก็บรักษาแบบอัตโนมัติไว้
หน้าต่างเทอร์มินัล
npm test # vitest, from this directory
npm run typecheck
npm run test:cross-service # this plugin and a live Dee Wan, both under wrangler dev; two to three minutes

npm test รวม test/conformance.test.ts ซึ่งรันกรณีของ protocol v1 จาก @dee-wan/plugin-conformance กับปลั๊กอินนี้: ตาราง outcome, สถานะ, flag ของ retry, capability และรูปแบบของ secret เทียบกับ schema ที่เผยแพร่, manifest เทียบกับ manifest schema ที่เผยแพร่, การจำแนกคำตอบ, activation, การแลกรหัส launch และการส่งคำสั่งผ่าน runner จริงกับ core ปลอมของแพ็กเกจ test/helpers/d1.ts คือ D1 shim ที่ใช้ better-sqlite3 และ apply ทุกไฟล์ใน migrations/

test/docs.test.ts รันตาราง Runner และ Time ข้างต้นผ่าน classify และ resolveLocalTime ดังนั้นเมื่อข้อมูลในตารางล้าสมัย การทดสอบจะล้มเหลว

test:cross-service รัน test/cross-service/cross-service.test.mjs มันเริ่ม admin Worker ของ Dee Wan และปลั๊กอินนี้ภายใต้ wrangler dev บนเครื่อง local ติดตั้งปลั๊กอินผ่าน Dee Wan และรันการเผยแพร่และการยกเลิกการเผยแพร่ตามกำหนดเวลา รวมถึงการปฏิเสธ ผ่าน protocol v1 Dee Wan เป็น checkout ภายนอก: ตั้ง DEE_WAN_CORE_DIR เป็น repository ของ Dee Wan ที่ติดตั้ง dependency แล้ว (node scripts/install-dependencies.mjs), build แพ็กเกจแล้ว (npm run build:packages) และสร้าง Prisma client แล้ว (npm --workspace backend run prisma:generate) ภายใน repository ของ Dee Wan ไม่ต้องตั้งตัวแปรนี้ การรันข้ามเซอร์วิสแบบอัตโนมัติ แสดงรายการสิ่งที่มันยืนยัน

.github/workflows/ci.yml คือ CI ของปลั๊กอินนี้เองสำหรับ repository ของมันเอง: ติดตั้ง, typecheck, npm test แล้วรันข้ามเซอร์วิสกับ checkout ของ Dee Wan ที่ vars.DEE_WAN_CORE_REF (ค่าเริ่มต้น main) GitHub ไม่รันมันขณะที่ไดเรกทอรีนี้อยู่ใน repository ของ Dee Wan และ CI ของ Dee Wan ไม่รันปลั๊กอินนี้

การย้ายต้องได้รับอำนาจอย่างชัดเจน: การสร้าง dee-wan-cms/dee-wan-scheduling, การ push ไปยังมัน และการเผยแพร่ @dee-wan/plugin-conformance เป็นการกระทำภายนอก เมื่อได้รับอำนาจแล้ว จาก root ของ repository ของ Dee Wan:

หน้าต่างเทอร์มินัล
# 1. Publish the conformance package (needs npm publish rights for the @dee-wan scope).
node scripts/install-dependencies.mjs
npm --prefix packages/plugin-conformance run build
cd packages/plugin-conformance
npm test
npm publish --access public
cd ../..
# 2. Split this directory with its history into a branch.
git subtree split --prefix=plugins/scheduling -b scheduling-split
# 3. Create the empty repository dee-wan-cms/dee-wan-scheduling on GitHub, then push the branch as main.
git push https://github.com/dee-wan-cms/dee-wan-scheduling.git scheduling-split:main
# 4. In a clone of the new repository: depend on the published package and commit a lockfile.
git clone https://github.com/dee-wan-cms/dee-wan-scheduling.git
cd dee-wan-scheduling
npm pkg set devDependencies.@dee-wan/plugin-conformance=^0.1.0
npm install
npm run typecheck
npm test
git add package.json package-lock.json
git commit -m "Depend on the published @dee-wan/plugin-conformance"
git push

จากนั้นใน repository นั้น: หาก dee-wan-cms/dee-wan เป็น private ให้เพิ่ม secret DEE_WAN_CORE_TOKEN ที่อ่านได้อย่างเดียว และตั้งตัวแปร DEE_WAN_CORE_REF เป็น ref ของ Dee Wan ที่จะทดสอบด้วยหากต้องการ ลิงก์ใน README นี้และใน docs/end-to-end.md ที่ชี้ไปยัง ../../docs/plugins/ ต้องเปลี่ยนเป็นลิงก์ไปยังเอกสาร Dee Wan ที่เผยแพร่แล้ว

การลบ plugins/scheduling ออกจาก repository ของ Dee Wan เป็นการเปลี่ยนแปลงแยกต่างหากที่นั่น ซึ่งจะนำ README นี้และ docs/end-to-end.md ออกจากเว็บไซต์เอกสารของ Dee Wan ด้วย (docs-site/allowlist.mjs และคำแปล ru และ th) และชี้ docs/plugins/*.md ไปยัง repository ใหม่


ยังไม่เปิดตัว (pre-MVP) และยังไม่มีเวอร์ชันเสถียร
คอมมิตซอร์สโค้ด: e207e63239a627fbd2ae9c414ef7c6eb19fcfe5a (มีการแก้ไขที่ยังไม่คอมมิต)
วันที่สร้างเอกสาร (UTC): 2026-10-21T09:28:24.214Z
คอมมิตสำหรับอัปเกรด: ระบุไม่ได้: มีการแก้ไขที่ยังไม่คอมมิต. บันทึกการเปลี่ยนแปลง