KIRO_TERMINAL_AWAKE ทำงานอย่างไร

ภาพประกอบ: KIRO_TERMINAL_AWAKE ทำงานอย่างไร

บทความ เริ่มต้นใช้งาน บอกวิธีติดตั้งและใช้งาน บทความนี้เจาะลึกว่าข้างในทำงานอย่างไร — ทำไมถึงใช้ launchd แทน daemon ที่รันค้าง ทำไม AppleScript ที่วางข้อความถึงเขียนแบบเฉพาะเจาะจง และแอปนี้ทำอะไรบ้างเพื่อไม่ให้ผูกติดกับเครื่องใดเครื่องหนึ่ง

ทำไมยิงครั้งเดียวแล้วเลิก ไม่ใช่ daemon ที่รันค้าง

เมื่อกด “ตั้ง AWAKE” แอปเขียน LaunchAgent plist ใหม่ที่มี StartCalendarInterval ตรงชั่วโมง/นาทีที่เลือก และ RunAtLoad: false — บอก launchd ให้ยิงแค่ครั้งเดียวตรงเวลานั้น ไม่ใช่ตอนเปิดเครื่อง จากนั้นโหลดเข้าระบบด้วย launchctl bootstrap gui/<uid> <plist>

จุดที่ตั้งใจคือ helper script ที่ launchd เรียกจะ unload ตัวเองทันทีหลังส่งข้อความสำเร็จ (launchctl bootout + ลบไฟล์ plist) — ถ้าไม่ทำแบบนี้ StartCalendarInterval แบบระบุ Hour/Minute จะยิงซ้ำทุกวันตรงเวลานั้นตลอดไป ซึ่งไม่ใช่พฤติกรรมที่ต้องการ (ตั้งครั้งเดียว ใช้ครั้งเดียว ไม่ใช่นาฬิกาปลุกรายวัน) ก่อนตั้งเวลาใหม่ทุกครั้ง แอปจะเรียก stopLoadedJobs() เพื่อเคลียร์ job เก่าที่อาจค้างอยู่ก่อนเสมอ

ข้อสำคัญ: การ unload ตัวเองเกิดขึ้นเฉพาะเส้นทางที่สำเร็จ — ในสคริปต์ cleanup_job ถูกเรียกที่บรรทัดสุดท้ายเท่านั้น ส่วนเส้นทาง error จบด้วย exit 1 โดยไม่เรียก cleanup และไม่มี trap ... EXIT มารับ ผลคือถ้ารอบนั้นส่งไม่สำเร็จ plist จะยังอยู่ และเพราะ StartCalendarInterval ระบุแค่ Hour/Minute ไม่ได้ระบุ Day launchd จะยิงซ้ำเวลาเดิมของวันถัดไปไปเรื่อยๆ จนกว่าจะสำเร็จหรือผู้ใช้กดยกเลิก — ตีความเป็น retry อัตโนมัติได้ แต่เป็นพฤติกรรมที่ผู้ใช้ควรรู้ เพราะกรณีล้มเหลวที่พบบ่อยที่สุด (ยังไม่ได้ให้สิทธิ์ Accessibility) มักเกิดตอนไม่มีใครอยู่เห็น notification พอดี

เส้นทางของการส่งข้อความหนึ่งครั้ง

launchd (ตรงเวลาที่ตั้งไว้)
   │
   ▼
awake_inject_kiro.sh
   │  open -a Kiro, รอ 1.5 วินาที
   ▼
osascript (AppleScript ฝังในสคริปต์)
   │  เก็บ clipboard เดิม → set clipboard เป็นข้อความ
   │  activate Kiro → key code 9 (Cmd+V) → key code 36 (Return)
   │  คืนค่า clipboard เดิมกลับ (สำเร็จหรือ error ก็คืนเสมอ)
   ▼
cleanup_job — bootout ตัวเอง + ลบ plist

การวางข้อความใช้ physical key code (key code 9 = ตำแหน่งปุ่ม V, key code 36 = ตำแหน่งปุ่ม Return) แทน keystroke "v" ที่ตีความตามตัวอักษรผ่าน input source ปัจจุบัน — ถ้าเครื่องเปลี่ยน input source ไปเป็นภาษาที่ไม่ใช่ภาษาอังกฤษ (เช่นสลับไปพิมพ์ไทยแล้วลืมสลับกลับ) keystroke "v" อาจ resolve ผิดตัวอักษรหรือพิมพ์อะไรแปลกๆ แทนที่จะกด Cmd+V จริง ส่วน physical key code ทำงานเหมือนกันเสมอไม่ว่า input source ที่ตั้งอยู่จะเป็นภาษาไหน

จุดที่ตั้งใจอีกอย่างคือ ไม่ส่ง Cmd+L ก่อน ทั้งที่ Kiro มีคำสั่ง kiroAgent.focusChatInput ผูกกับปุ่มนั้น — เพราะ Cmd+L จะพาโฟกัสไปที่ช่องแชทของ Kiro แทนที่จะเป็น terminal panel ซึ่งขัดกับจุดประสงค์จริงของเครื่องมือนี้ (สั่งงานต่อใน terminal ที่ agent กำลังทำงานอยู่ ไม่ใช่เปิดแชทใหม่) การออกแบบนี้จึงวางข้อความลง panel ที่ focus อยู่ ณ ตอนนั้นตรงๆ — ผู้ใช้ต้องเปิดและ focus ไว้ที่ terminal panel เองก่อนถึงเวลาที่ตั้งไว้

Timeout 8 วินาทีที่ตั้งใจ กับการแยกสาเหตุ error สองแบบ

AppleScript ทั้งก้อนอยู่ใน with timeout of 8 seconds อย่างชัดเจน — ถ้า System Events หรือ Kiro ไม่ตอบภายใน 8 วินาที จะ error ทันทีแทนที่จะปล่อยให้ค้างไปตาม default ของ macOS ที่ประมาณ 60-120 วินาทีต่อ AppleEvent ที่ไม่มีใครตอบ ทำให้ log บอกได้ชัดว่าเกิดอะไรขึ้นโดยไม่ต้องรอค้างนาน

สคริปต์แยกสาเหตุ error ออกเป็นสองแบบจาก error code ที่ osascript คืนมา:

  • -1712 — timeout เกิดเพราะไม่มีใครอยู่กด Allow ตอน macOS ขอสิทธิ์ครั้งแรก (TCC prompt ต้องมีคนตอบ ไม่ตอบเองอัตโนมัติได้)
  • 1002 หรือข้อความ not allowed to send keystrokes — ยังไม่ได้ให้สิทธิ์ Accessibility เลย

ทั้งสองกรณี log จะเขียน hint บอกตรงๆ ว่าต้องไปเปิดที่ System Settings → Privacy & Security → Accessibility ให้ osascript แล้วส่ง notification เตือนด้วย เพื่อให้ผู้ใช้เห็นทันทีว่าทำไมข้อความไม่ถูกส่ง แทนที่จะเงียบและดูเหมือนไม่มีอะไรเกิดขึ้น

ทำไมไม่ผูกกับเครื่องไหนเครื่องหนึ่ง

homeURL, appSupportURL, และ path ที่เกี่ยวข้องทั้งหมด resolve จาก FileManager.default.homeDirectoryForCurrentUser ที่ runtime เสมอ ไม่มี username หรือ absolute path เฉพาะเครื่องฝังอยู่ในซอร์สโค้ดเลย — helper ที่ build ได้ตัวเดียวใช้ได้กับ Mac เครื่องไหนก็ได้ทันทีหลัง clone

การ build ใช้ codesign --sign - (ad-hoc signing) ไม่ผูกกับ developer certificate ของใครเลย และ build สร้างเป็น universal binary (lipo รวม arm64 + x86_64) ในขั้นตอนเดียว ไม่ต้องเลือกสถาปัตยกรรมเอง

ทำไมเรื่องนี้ถึงสำคัญ

เครื่องมือ agent ส่วนใหญ่แก้ปัญหา “agent ทำอะไรได้บ้าง” แต่ปัญหาจริงบางทีเป็นเรื่องง่ายกว่านั้นมาก: agent ทำงานได้ปกติทุกอย่าง เพียงแต่ไม่มีใครไปกดปุ่มต่อให้ตอนที่ควรกด KIRO_TERMINAL_AWAKE ไม่ได้พยายามฉลาดขึ้นหรือเข้าใจบริบทงานเลยแม้แต่น้อย — มันแค่รับประกันว่าข้อความหนึ่งบรรทัดจะถูกส่งตรงเวลา แม่นยำ และได้แค่ครั้งเดียวตามที่ตั้งไว้ ซึ่งเพียงพอแล้วสำหรับปัญหาที่แก้อยู่

อ่านเพิ่มเติม

ความคิดเห็น

กำลังโหลดความคิดเห็น...