@songsid/agend 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/README.md +210 -0
  2. package/README.zh-TW.md +134 -0
  3. package/dist/access-path.d.ts +10 -0
  4. package/dist/access-path.js +32 -0
  5. package/dist/access-path.js.map +1 -0
  6. package/dist/adapter-world.d.ts +25 -0
  7. package/dist/adapter-world.js +41 -0
  8. package/dist/adapter-world.js.map +1 -0
  9. package/dist/agent-cli-instructions.md +50 -0
  10. package/dist/agent-cli.d.ts +2 -0
  11. package/dist/agent-cli.js +200 -0
  12. package/dist/agent-cli.js.map +1 -0
  13. package/dist/agent-endpoint.d.ts +25 -0
  14. package/dist/agent-endpoint.js +162 -0
  15. package/dist/agent-endpoint.js.map +1 -0
  16. package/dist/backend/antigravity.d.ts +17 -0
  17. package/dist/backend/antigravity.js +98 -0
  18. package/dist/backend/antigravity.js.map +1 -0
  19. package/dist/backend/claude-code.d.ts +23 -0
  20. package/dist/backend/claude-code.js +171 -0
  21. package/dist/backend/claude-code.js.map +1 -0
  22. package/dist/backend/codex.d.ts +18 -0
  23. package/dist/backend/codex.js +160 -0
  24. package/dist/backend/codex.js.map +1 -0
  25. package/dist/backend/factory.d.ts +2 -0
  26. package/dist/backend/factory.js +28 -0
  27. package/dist/backend/factory.js.map +1 -0
  28. package/dist/backend/gemini-cli.d.ts +17 -0
  29. package/dist/backend/gemini-cli.js +163 -0
  30. package/dist/backend/gemini-cli.js.map +1 -0
  31. package/dist/backend/index.d.ts +7 -0
  32. package/dist/backend/index.js +7 -0
  33. package/dist/backend/index.js.map +1 -0
  34. package/dist/backend/kiro.d.ts +17 -0
  35. package/dist/backend/kiro.js +147 -0
  36. package/dist/backend/kiro.js.map +1 -0
  37. package/dist/backend/marker-utils.d.ts +13 -0
  38. package/dist/backend/marker-utils.js +64 -0
  39. package/dist/backend/marker-utils.js.map +1 -0
  40. package/dist/backend/mock.d.ts +25 -0
  41. package/dist/backend/mock.js +85 -0
  42. package/dist/backend/mock.js.map +1 -0
  43. package/dist/backend/opencode.d.ts +16 -0
  44. package/dist/backend/opencode.js +136 -0
  45. package/dist/backend/opencode.js.map +1 -0
  46. package/dist/backend/types.d.ts +86 -0
  47. package/dist/backend/types.js +33 -0
  48. package/dist/backend/types.js.map +1 -0
  49. package/dist/channel/access-manager.d.ts +18 -0
  50. package/dist/channel/access-manager.js +153 -0
  51. package/dist/channel/access-manager.js.map +1 -0
  52. package/dist/channel/adapters/telegram.d.ts +63 -0
  53. package/dist/channel/adapters/telegram.js +646 -0
  54. package/dist/channel/adapters/telegram.js.map +1 -0
  55. package/dist/channel/attachment-handler.d.ts +15 -0
  56. package/dist/channel/attachment-handler.js +88 -0
  57. package/dist/channel/attachment-handler.js.map +1 -0
  58. package/dist/channel/factory.d.ts +12 -0
  59. package/dist/channel/factory.js +67 -0
  60. package/dist/channel/factory.js.map +1 -0
  61. package/dist/channel/ipc-bridge.d.ts +26 -0
  62. package/dist/channel/ipc-bridge.js +220 -0
  63. package/dist/channel/ipc-bridge.js.map +1 -0
  64. package/dist/channel/mcp-server.d.ts +10 -0
  65. package/dist/channel/mcp-server.js +288 -0
  66. package/dist/channel/mcp-server.js.map +1 -0
  67. package/dist/channel/mcp-tools.d.ts +17 -0
  68. package/dist/channel/mcp-tools.js +110 -0
  69. package/dist/channel/mcp-tools.js.map +1 -0
  70. package/dist/channel/message-bus.d.ts +17 -0
  71. package/dist/channel/message-bus.js +86 -0
  72. package/dist/channel/message-bus.js.map +1 -0
  73. package/dist/channel/message-queue.d.ts +39 -0
  74. package/dist/channel/message-queue.js +253 -0
  75. package/dist/channel/message-queue.js.map +1 -0
  76. package/dist/channel/tool-router.d.ts +6 -0
  77. package/dist/channel/tool-router.js +75 -0
  78. package/dist/channel/tool-router.js.map +1 -0
  79. package/dist/channel/tool-tracker.d.ts +13 -0
  80. package/dist/channel/tool-tracker.js +58 -0
  81. package/dist/channel/tool-tracker.js.map +1 -0
  82. package/dist/channel/types.d.ts +118 -0
  83. package/dist/channel/types.js +2 -0
  84. package/dist/channel/types.js.map +1 -0
  85. package/dist/chat-export.d.ts +4 -0
  86. package/dist/chat-export.js +91 -0
  87. package/dist/chat-export.js.map +1 -0
  88. package/dist/classic-channel-manager.d.ts +59 -0
  89. package/dist/classic-channel-manager.js +193 -0
  90. package/dist/classic-channel-manager.js.map +1 -0
  91. package/dist/cli.d.ts +2 -0
  92. package/dist/cli.js +1833 -0
  93. package/dist/cli.js.map +1 -0
  94. package/dist/config.d.ts +9 -0
  95. package/dist/config.js +118 -0
  96. package/dist/config.js.map +1 -0
  97. package/dist/context-guardian.d.ts +26 -0
  98. package/dist/context-guardian.js +73 -0
  99. package/dist/context-guardian.js.map +1 -0
  100. package/dist/cost-guard.d.ts +36 -0
  101. package/dist/cost-guard.js +147 -0
  102. package/dist/cost-guard.js.map +1 -0
  103. package/dist/daemon-entry.d.ts +1 -0
  104. package/dist/daemon-entry.js +29 -0
  105. package/dist/daemon-entry.js.map +1 -0
  106. package/dist/daemon.d.ts +152 -0
  107. package/dist/daemon.js +1714 -0
  108. package/dist/daemon.js.map +1 -0
  109. package/dist/daily-summary.d.ts +13 -0
  110. package/dist/daily-summary.js +55 -0
  111. package/dist/daily-summary.js.map +1 -0
  112. package/dist/event-log.d.ts +36 -0
  113. package/dist/event-log.js +100 -0
  114. package/dist/event-log.js.map +1 -0
  115. package/dist/export-import.d.ts +2 -0
  116. package/dist/export-import.js +162 -0
  117. package/dist/export-import.js.map +1 -0
  118. package/dist/fleet-context.d.ts +61 -0
  119. package/dist/fleet-context.js +4 -0
  120. package/dist/fleet-context.js.map +1 -0
  121. package/dist/fleet-dashboard-html.d.ts +6 -0
  122. package/dist/fleet-dashboard-html.js +443 -0
  123. package/dist/fleet-dashboard-html.js.map +1 -0
  124. package/dist/fleet-health-server.d.ts +35 -0
  125. package/dist/fleet-health-server.js +290 -0
  126. package/dist/fleet-health-server.js.map +1 -0
  127. package/dist/fleet-instructions.d.ts +5 -0
  128. package/dist/fleet-instructions.js +161 -0
  129. package/dist/fleet-instructions.js.map +1 -0
  130. package/dist/fleet-manager.d.ts +212 -0
  131. package/dist/fleet-manager.js +3655 -0
  132. package/dist/fleet-manager.js.map +1 -0
  133. package/dist/fleet-rpc-handlers.d.ts +42 -0
  134. package/dist/fleet-rpc-handlers.js +356 -0
  135. package/dist/fleet-rpc-handlers.js.map +1 -0
  136. package/dist/fleet-system-prompt.d.ts +11 -0
  137. package/dist/fleet-system-prompt.js +61 -0
  138. package/dist/fleet-system-prompt.js.map +1 -0
  139. package/dist/general-knowledge/skills.md +177 -0
  140. package/dist/hang-detector.d.ts +16 -0
  141. package/dist/hang-detector.js +53 -0
  142. package/dist/hang-detector.js.map +1 -0
  143. package/dist/index.d.ts +8 -0
  144. package/dist/index.js +6 -0
  145. package/dist/index.js.map +1 -0
  146. package/dist/instance-lifecycle.d.ts +90 -0
  147. package/dist/instance-lifecycle.js +592 -0
  148. package/dist/instance-lifecycle.js.map +1 -0
  149. package/dist/instructions.d.ts +15 -0
  150. package/dist/instructions.js +90 -0
  151. package/dist/instructions.js.map +1 -0
  152. package/dist/logger.d.ts +7 -0
  153. package/dist/logger.js +84 -0
  154. package/dist/logger.js.map +1 -0
  155. package/dist/outbound-handlers.d.ts +51 -0
  156. package/dist/outbound-handlers.js +739 -0
  157. package/dist/outbound-handlers.js.map +1 -0
  158. package/dist/outbound-schemas.d.ts +238 -0
  159. package/dist/outbound-schemas.js +248 -0
  160. package/dist/outbound-schemas.js.map +1 -0
  161. package/dist/paths.d.ts +10 -0
  162. package/dist/paths.js +42 -0
  163. package/dist/paths.js.map +1 -0
  164. package/dist/plugin/agend/.claude-plugin/plugin.json +5 -0
  165. package/dist/quickstart.d.ts +1 -0
  166. package/dist/quickstart.js +595 -0
  167. package/dist/quickstart.js.map +1 -0
  168. package/dist/routing-engine.d.ts +22 -0
  169. package/dist/routing-engine.js +44 -0
  170. package/dist/routing-engine.js.map +1 -0
  171. package/dist/safe-async.d.ts +6 -0
  172. package/dist/safe-async.js +20 -0
  173. package/dist/safe-async.js.map +1 -0
  174. package/dist/scheduler/db.d.ts +37 -0
  175. package/dist/scheduler/db.js +360 -0
  176. package/dist/scheduler/db.js.map +1 -0
  177. package/dist/scheduler/db.test.d.ts +1 -0
  178. package/dist/scheduler/db.test.js +92 -0
  179. package/dist/scheduler/db.test.js.map +1 -0
  180. package/dist/scheduler/index.d.ts +4 -0
  181. package/dist/scheduler/index.js +4 -0
  182. package/dist/scheduler/index.js.map +1 -0
  183. package/dist/scheduler/scheduler.d.ts +44 -0
  184. package/dist/scheduler/scheduler.js +197 -0
  185. package/dist/scheduler/scheduler.js.map +1 -0
  186. package/dist/scheduler/scheduler.test.d.ts +1 -0
  187. package/dist/scheduler/scheduler.test.js +119 -0
  188. package/dist/scheduler/scheduler.test.js.map +1 -0
  189. package/dist/scheduler/types.d.ts +107 -0
  190. package/dist/scheduler/types.js +7 -0
  191. package/dist/scheduler/types.js.map +1 -0
  192. package/dist/service-installer.d.ts +17 -0
  193. package/dist/service-installer.js +182 -0
  194. package/dist/service-installer.js.map +1 -0
  195. package/dist/setup-wizard.d.ts +48 -0
  196. package/dist/setup-wizard.js +701 -0
  197. package/dist/setup-wizard.js.map +1 -0
  198. package/dist/statusline-watcher.d.ts +34 -0
  199. package/dist/statusline-watcher.js +73 -0
  200. package/dist/statusline-watcher.js.map +1 -0
  201. package/dist/stt.d.ts +10 -0
  202. package/dist/stt.js +33 -0
  203. package/dist/stt.js.map +1 -0
  204. package/dist/tmux-control.d.ts +52 -0
  205. package/dist/tmux-control.js +207 -0
  206. package/dist/tmux-control.js.map +1 -0
  207. package/dist/tmux-manager.d.ts +44 -0
  208. package/dist/tmux-manager.js +218 -0
  209. package/dist/tmux-manager.js.map +1 -0
  210. package/dist/topic-archiver.d.ts +40 -0
  211. package/dist/topic-archiver.js +103 -0
  212. package/dist/topic-archiver.js.map +1 -0
  213. package/dist/topic-commands.d.ts +28 -0
  214. package/dist/topic-commands.js +359 -0
  215. package/dist/topic-commands.js.map +1 -0
  216. package/dist/transcript-monitor.d.ts +23 -0
  217. package/dist/transcript-monitor.js +164 -0
  218. package/dist/transcript-monitor.js.map +1 -0
  219. package/dist/types.d.ts +211 -0
  220. package/dist/types.js +2 -0
  221. package/dist/types.js.map +1 -0
  222. package/dist/ui/dashboard.html +719 -0
  223. package/dist/web-api.d.ts +101 -0
  224. package/dist/web-api.js +648 -0
  225. package/dist/web-api.js.map +1 -0
  226. package/dist/webhook-emitter.d.ts +15 -0
  227. package/dist/webhook-emitter.js +41 -0
  228. package/dist/webhook-emitter.js.map +1 -0
  229. package/dist/workflow-templates/default.md +35 -0
  230. package/package.json +76 -0
  231. package/templates/launchd.plist.ejs +31 -0
  232. package/templates/systemd.service.ejs +16 -0
@@ -0,0 +1,22 @@
1
+ import type { FleetConfig } from "./types.js";
2
+ import { type RouteTarget } from "./fleet-context.js";
3
+ /**
4
+ * Manages the topic→instance routing table.
5
+ * All topic IDs are normalized to strings to avoid snowflake precision loss.
6
+ */
7
+ export declare class RoutingEngine {
8
+ private table;
9
+ /** Rebuild routing table from fleet config. Returns summary string for logging. */
10
+ rebuild(config: FleetConfig): string;
11
+ /** Resolve a thread ID to a route target. */
12
+ resolve(threadId: string): RouteTarget | undefined;
13
+ /** Register a new topic→instance mapping. */
14
+ register(topicId: number | string, target: RouteTarget): void;
15
+ /** Remove a topic from the routing table. */
16
+ unregister(topicId: number | string): void;
17
+ /** Iterate over all routes. */
18
+ entries(): IterableIterator<[string, RouteTarget]>;
19
+ /** Get the underlying map (for FleetContext compatibility). */
20
+ get map(): Map<string, RouteTarget>;
21
+ get size(): number;
22
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Manages the topic→instance routing table.
3
+ * All topic IDs are normalized to strings to avoid snowflake precision loss.
4
+ */
5
+ export class RoutingEngine {
6
+ table = new Map();
7
+ /** Rebuild routing table from fleet config. Returns summary string for logging. */
8
+ rebuild(config) {
9
+ this.table.clear();
10
+ for (const [name, inst] of Object.entries(config.instances)) {
11
+ if (inst.topic_id != null) {
12
+ this.table.set(String(inst.topic_id), {
13
+ kind: inst.general_topic ? "general" : "instance",
14
+ name,
15
+ });
16
+ }
17
+ }
18
+ return [...this.table.entries()].map(([tid, t]) => `#${tid}→${t.name}`).join(", ");
19
+ }
20
+ /** Resolve a thread ID to a route target. */
21
+ resolve(threadId) {
22
+ return this.table.get(threadId);
23
+ }
24
+ /** Register a new topic→instance mapping. */
25
+ register(topicId, target) {
26
+ this.table.set(String(topicId), target);
27
+ }
28
+ /** Remove a topic from the routing table. */
29
+ unregister(topicId) {
30
+ this.table.delete(String(topicId));
31
+ }
32
+ /** Iterate over all routes. */
33
+ entries() {
34
+ return this.table.entries();
35
+ }
36
+ /** Get the underlying map (for FleetContext compatibility). */
37
+ get map() {
38
+ return this.table;
39
+ }
40
+ get size() {
41
+ return this.table.size;
42
+ }
43
+ }
44
+ //# sourceMappingURL=routing-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing-engine.js","sourceRoot":"","sources":["../src/routing-engine.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,OAAO,aAAa;IAChB,KAAK,GAAG,IAAI,GAAG,EAAuB,CAAC;IAE/C,mFAAmF;IACnF,OAAO,CAAC,MAAmB;QACzB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5D,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;gBAC1B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;oBACpC,IAAI,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU;oBACjD,IAAI;iBACL,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrF,CAAC;IAED,6CAA6C;IAC7C,OAAO,CAAC,QAAgB;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAED,6CAA6C;IAC7C,QAAQ,CAAC,OAAwB,EAAE,MAAmB;QACpD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;IAC1C,CAAC;IAED,6CAA6C;IAC7C,UAAU,CAAC,OAAwB;QACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACrC,CAAC;IAED,+BAA+B;IAC/B,OAAO;QACL,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IAC9B,CAAC;IAED,+DAA+D;IAC/D,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ import type { Logger } from "./logger.js";
2
+ /**
3
+ * Wrap an async EventEmitter callback so unhandled errors are logged
4
+ * instead of crashing the process via unhandled rejection.
5
+ */
6
+ export declare function safeHandler<T extends unknown[]>(fn: (...args: T) => void | Promise<void>, logger: Logger, context: string): (...args: T) => void;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Wrap an async EventEmitter callback so unhandled errors are logged
3
+ * instead of crashing the process via unhandled rejection.
4
+ */
5
+ export function safeHandler(fn, logger, context) {
6
+ return (...args) => {
7
+ try {
8
+ const result = fn(...args);
9
+ if (result && typeof result.catch === "function") {
10
+ result.catch((err) => {
11
+ logger.error({ err, context }, "Unhandled error in async handler");
12
+ });
13
+ }
14
+ }
15
+ catch (err) {
16
+ logger.error({ err, context }, "Unhandled error in sync handler");
17
+ }
18
+ };
19
+ }
20
+ //# sourceMappingURL=safe-async.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-async.js","sourceRoot":"","sources":["../src/safe-async.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,EAAwC,EACxC,MAAc,EACd,OAAe;IAEf,OAAO,CAAC,GAAG,IAAO,EAAE,EAAE;QACpB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;YAC3B,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;gBACjD,MAAM,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;oBAC5B,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,kCAAkC,CAAC,CAAC;gBACrE,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,iCAAiC,CAAC,CAAC;QACpE,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,37 @@
1
+ import type { Schedule, ScheduleRun, CreateScheduleParams, UpdateScheduleParams, Decision, CreateDecisionParams, UpdateDecisionParams, Task, CreateTaskParams, UpdateTaskParams } from "./types.js";
2
+ export declare class SchedulerDb {
3
+ private db;
4
+ constructor(dbPath: string);
5
+ private rowToSchedule;
6
+ create(params: CreateScheduleParams, maxSchedules?: number): Schedule;
7
+ get(id: string): Schedule | null;
8
+ list(target?: string): Schedule[];
9
+ update(id: string, params: UpdateScheduleParams): Schedule;
10
+ delete(id: string): void;
11
+ deleteByInstanceOrThread(instanceName: string, threadId: string): number;
12
+ recordRun(scheduleId: string, status: string, detail?: string): void;
13
+ getRuns(scheduleId: string, limit?: number): ScheduleRun[];
14
+ pruneOldRuns(days?: number): void;
15
+ private rowToDecision;
16
+ createDecision(params: CreateDecisionParams): Decision;
17
+ getDecision(id: string): Decision | null;
18
+ listDecisions(projectRoot: string, opts?: {
19
+ includeArchived?: boolean;
20
+ tags?: string[];
21
+ }): Decision[];
22
+ updateDecision(id: string, params: UpdateDecisionParams): Decision;
23
+ archiveDecision(id: string): void;
24
+ supersedeDecision(oldId: string, newId: string): void;
25
+ pruneExpiredDecisions(): number;
26
+ private rowToTask;
27
+ createTask(params: CreateTaskParams): Task;
28
+ getTask(id: string): Task | null;
29
+ listTasks(opts?: {
30
+ assignee?: string;
31
+ status?: string;
32
+ }): Task[];
33
+ updateTask(id: string, params: UpdateTaskParams): Task;
34
+ claimTask(id: string, assignee: string): Task;
35
+ completeTask(id: string, result?: string): Task;
36
+ close(): void;
37
+ }
@@ -0,0 +1,360 @@
1
+ import Database from "better-sqlite3";
2
+ import { randomUUID } from "node:crypto";
3
+ export class SchedulerDb {
4
+ db;
5
+ constructor(dbPath) {
6
+ this.db = new Database(dbPath);
7
+ this.db.pragma("journal_mode = WAL");
8
+ this.db.pragma("foreign_keys = ON");
9
+ this.db.exec(`
10
+ CREATE TABLE IF NOT EXISTS schedules (
11
+ id TEXT PRIMARY KEY,
12
+ cron TEXT NOT NULL,
13
+ message TEXT NOT NULL,
14
+ source TEXT NOT NULL,
15
+ target TEXT NOT NULL,
16
+ reply_chat_id TEXT NOT NULL,
17
+ reply_thread_id TEXT,
18
+ label TEXT,
19
+ enabled INTEGER DEFAULT 1,
20
+ timezone TEXT DEFAULT 'Asia/Taipei',
21
+ created_at TEXT NOT NULL,
22
+ last_triggered_at TEXT,
23
+ last_status TEXT
24
+ );
25
+ CREATE TABLE IF NOT EXISTS schedule_runs (
26
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
27
+ schedule_id TEXT NOT NULL REFERENCES schedules(id) ON DELETE CASCADE,
28
+ triggered_at TEXT NOT NULL DEFAULT (datetime('now')),
29
+ status TEXT NOT NULL,
30
+ detail TEXT
31
+ );
32
+ CREATE INDEX IF NOT EXISTS idx_schedule_runs_schedule_id ON schedule_runs(schedule_id);
33
+ CREATE TABLE IF NOT EXISTS decisions (
34
+ id TEXT PRIMARY KEY,
35
+ project_root TEXT NOT NULL,
36
+ scope TEXT NOT NULL DEFAULT 'project',
37
+ title TEXT NOT NULL,
38
+ content TEXT NOT NULL,
39
+ tags TEXT,
40
+ status TEXT NOT NULL DEFAULT 'active',
41
+ superseded_by TEXT,
42
+ created_by TEXT NOT NULL,
43
+ created_at TEXT NOT NULL,
44
+ expires_at TEXT,
45
+ updated_at TEXT NOT NULL
46
+ );
47
+ CREATE INDEX IF NOT EXISTS idx_decisions_project ON decisions(project_root);
48
+ CREATE INDEX IF NOT EXISTS idx_decisions_status ON decisions(status);
49
+ `);
50
+ // Migration: add scope column to existing decisions tables that lack it
51
+ const cols = this.db.prepare("PRAGMA table_info(decisions)").all();
52
+ if (cols.length > 0 && !cols.some(c => c.name === "scope")) {
53
+ this.db.exec("ALTER TABLE decisions ADD COLUMN scope TEXT NOT NULL DEFAULT 'project'");
54
+ }
55
+ this.db.exec("CREATE INDEX IF NOT EXISTS idx_decisions_scope ON decisions(scope)");
56
+ // Tasks table
57
+ this.db.exec(`
58
+ CREATE TABLE IF NOT EXISTS tasks (
59
+ id TEXT PRIMARY KEY,
60
+ title TEXT NOT NULL,
61
+ description TEXT,
62
+ status TEXT NOT NULL DEFAULT 'open',
63
+ priority TEXT NOT NULL DEFAULT 'normal',
64
+ assignee TEXT,
65
+ created_by TEXT NOT NULL,
66
+ depends_on TEXT,
67
+ result TEXT,
68
+ created_at TEXT NOT NULL,
69
+ updated_at TEXT NOT NULL
70
+ );
71
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
72
+ CREATE INDEX IF NOT EXISTS idx_tasks_assignee ON tasks(assignee);
73
+ `);
74
+ }
75
+ rowToSchedule(row) {
76
+ return {
77
+ id: row.id,
78
+ cron: row.cron,
79
+ message: row.message,
80
+ source: row.source,
81
+ target: row.target,
82
+ reply_chat_id: row.reply_chat_id,
83
+ reply_thread_id: row.reply_thread_id,
84
+ label: row.label,
85
+ enabled: row.enabled === 1,
86
+ timezone: row.timezone,
87
+ created_at: row.created_at,
88
+ last_triggered_at: row.last_triggered_at,
89
+ last_status: row.last_status,
90
+ };
91
+ }
92
+ create(params, maxSchedules = 100) {
93
+ const count = this.db.prepare("SELECT COUNT(*) as c FROM schedules").get();
94
+ if (count.c >= maxSchedules) {
95
+ throw new Error(`Schedule limit reached (${maxSchedules}). Delete existing schedules first.`);
96
+ }
97
+ const id = randomUUID();
98
+ const now = new Date().toISOString();
99
+ this.db.prepare(`
100
+ INSERT INTO schedules (id, cron, message, source, target, reply_chat_id, reply_thread_id, label, timezone, created_at)
101
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
102
+ `).run(id, params.cron, params.message, params.source, params.target, params.reply_chat_id, params.reply_thread_id, params.label ?? null, params.timezone ?? "Asia/Taipei", now);
103
+ return this.get(id);
104
+ }
105
+ get(id) {
106
+ const row = this.db.prepare("SELECT * FROM schedules WHERE id = ?").get(id);
107
+ return row ? this.rowToSchedule(row) : null;
108
+ }
109
+ list(target) {
110
+ const rows = target
111
+ ? this.db.prepare("SELECT * FROM schedules WHERE target = ? ORDER BY created_at").all(target)
112
+ : this.db.prepare("SELECT * FROM schedules ORDER BY created_at").all();
113
+ return rows.map((r) => this.rowToSchedule(r));
114
+ }
115
+ update(id, params) {
116
+ // Wrap SELECT-modify-UPDATE-SELECT in a transaction so concurrent updates
117
+ // can't interleave (the second writer would otherwise clobber the first
118
+ // based on a stale read).
119
+ return this.db.transaction(() => {
120
+ const existing = this.get(id);
121
+ if (!existing)
122
+ throw new Error(`Schedule "${id}" not found`);
123
+ const sets = [];
124
+ const values = [];
125
+ if (params.cron !== undefined) {
126
+ sets.push("cron = ?");
127
+ values.push(params.cron);
128
+ }
129
+ if (params.message !== undefined) {
130
+ sets.push("message = ?");
131
+ values.push(params.message);
132
+ }
133
+ if (params.target !== undefined) {
134
+ sets.push("target = ?");
135
+ values.push(params.target);
136
+ }
137
+ if (params.label !== undefined) {
138
+ sets.push("label = ?");
139
+ values.push(params.label);
140
+ }
141
+ if (params.timezone !== undefined) {
142
+ sets.push("timezone = ?");
143
+ values.push(params.timezone);
144
+ }
145
+ if (params.enabled !== undefined) {
146
+ sets.push("enabled = ?");
147
+ values.push(params.enabled ? 1 : 0);
148
+ }
149
+ if (sets.length > 0) {
150
+ values.push(id);
151
+ this.db.prepare(`UPDATE schedules SET ${sets.join(", ")} WHERE id = ?`).run(...values);
152
+ }
153
+ return this.get(id) ?? existing;
154
+ })();
155
+ }
156
+ delete(id) {
157
+ this.db.prepare("DELETE FROM schedules WHERE id = ?").run(id);
158
+ }
159
+ deleteByInstanceOrThread(instanceName, threadId) {
160
+ const result = this.db.prepare("DELETE FROM schedules WHERE target = ? OR reply_thread_id = ?").run(instanceName, threadId);
161
+ return result.changes;
162
+ }
163
+ recordRun(scheduleId, status, detail) {
164
+ this.db.prepare("INSERT INTO schedule_runs (schedule_id, status, detail) VALUES (?, ?, ?)").run(scheduleId, status, detail ?? null);
165
+ this.db.prepare("UPDATE schedules SET last_triggered_at = datetime('now'), last_status = ? WHERE id = ?").run(status, scheduleId);
166
+ }
167
+ getRuns(scheduleId, limit = 50) {
168
+ return this.db.prepare("SELECT * FROM schedule_runs WHERE schedule_id = ? ORDER BY triggered_at DESC, id DESC LIMIT ?").all(scheduleId, limit);
169
+ }
170
+ pruneOldRuns(days = 30) {
171
+ this.db.prepare("DELETE FROM schedule_runs WHERE triggered_at < datetime('now', '-' || ? || ' days')").run(days);
172
+ }
173
+ // ── Decisions ───────────────────────────────────────────────
174
+ rowToDecision(row) {
175
+ return {
176
+ id: row.id,
177
+ project_root: row.project_root,
178
+ scope: row.scope ?? "project",
179
+ title: row.title,
180
+ content: row.content,
181
+ tags: row.tags ? JSON.parse(row.tags) : [],
182
+ status: row.status,
183
+ superseded_by: row.superseded_by,
184
+ created_by: row.created_by,
185
+ created_at: row.created_at,
186
+ expires_at: row.expires_at,
187
+ updated_at: row.updated_at,
188
+ };
189
+ }
190
+ createDecision(params) {
191
+ const id = randomUUID();
192
+ const now = new Date().toISOString();
193
+ const ttl = params.ttl_days ?? 0; // default: permanent
194
+ const expiresAt = ttl > 0 ? new Date(Date.now() + ttl * 86_400_000).toISOString() : null;
195
+ const scope = params.scope ?? "project";
196
+ this.db.prepare(`
197
+ INSERT INTO decisions (id, project_root, scope, title, content, tags, created_by, created_at, expires_at, updated_at)
198
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
199
+ `).run(id, params.project_root, scope, params.title, params.content, params.tags?.length ? JSON.stringify(params.tags) : null, params.created_by, now, expiresAt, now);
200
+ if (params.supersedes) {
201
+ this.supersedeDecision(params.supersedes, id);
202
+ }
203
+ return this.getDecision(id);
204
+ }
205
+ getDecision(id) {
206
+ const row = this.db.prepare("SELECT * FROM decisions WHERE id = ?").get(id);
207
+ return row ? this.rowToDecision(row) : null;
208
+ }
209
+ listDecisions(projectRoot, opts) {
210
+ // Return project-scoped decisions for this root + all fleet-scoped decisions
211
+ let sql = "SELECT * FROM decisions WHERE ((project_root = ? AND scope = 'project') OR scope = 'fleet')";
212
+ const values = [projectRoot];
213
+ if (!opts?.includeArchived) {
214
+ sql += " AND status = 'active'";
215
+ }
216
+ // Fleet decisions first, then project, newest first within each group
217
+ sql += " ORDER BY CASE scope WHEN 'fleet' THEN 0 ELSE 1 END, created_at DESC";
218
+ let rows = this.db.prepare(sql).all(...values);
219
+ if (opts?.tags?.length) {
220
+ rows = rows.filter(r => {
221
+ const tags = r.tags ? JSON.parse(r.tags) : [];
222
+ return opts.tags.some(t => tags.includes(t));
223
+ });
224
+ }
225
+ return rows.map(r => this.rowToDecision(r));
226
+ }
227
+ updateDecision(id, params) {
228
+ const existing = this.getDecision(id);
229
+ if (!existing)
230
+ throw new Error(`Decision "${id}" not found`);
231
+ const sets = ["updated_at = ?"];
232
+ const values = [new Date().toISOString()];
233
+ if (params.content !== undefined) {
234
+ sets.push("content = ?");
235
+ values.push(params.content);
236
+ }
237
+ if (params.tags !== undefined) {
238
+ sets.push("tags = ?");
239
+ values.push(JSON.stringify(params.tags));
240
+ }
241
+ if (params.ttl_days !== undefined) {
242
+ const expiresAt = params.ttl_days > 0 ? new Date(Date.now() + params.ttl_days * 86_400_000).toISOString() : null;
243
+ sets.push("expires_at = ?");
244
+ values.push(expiresAt);
245
+ }
246
+ values.push(id);
247
+ this.db.prepare(`UPDATE decisions SET ${sets.join(", ")} WHERE id = ?`).run(...values);
248
+ return this.getDecision(id);
249
+ }
250
+ archiveDecision(id) {
251
+ this.db.prepare("UPDATE decisions SET status = 'archived', updated_at = ? WHERE id = ?").run(new Date().toISOString(), id);
252
+ }
253
+ supersedeDecision(oldId, newId) {
254
+ this.db.prepare("UPDATE decisions SET status = 'superseded', superseded_by = ?, updated_at = ? WHERE id = ?")
255
+ .run(newId, new Date().toISOString(), oldId);
256
+ }
257
+ pruneExpiredDecisions() {
258
+ const result = this.db.prepare("UPDATE decisions SET status = 'archived', updated_at = ? WHERE status = 'active' AND expires_at IS NOT NULL AND expires_at < ?")
259
+ .run(new Date().toISOString(), new Date().toISOString());
260
+ return result.changes;
261
+ }
262
+ // ── Tasks ──────────────────────────────────────────────────
263
+ rowToTask(row) {
264
+ return {
265
+ id: row.id,
266
+ title: row.title,
267
+ description: row.description,
268
+ status: row.status,
269
+ priority: row.priority,
270
+ assignee: row.assignee,
271
+ created_by: row.created_by,
272
+ depends_on: row.depends_on ? JSON.parse(row.depends_on) : [],
273
+ result: row.result,
274
+ created_at: row.created_at,
275
+ updated_at: row.updated_at,
276
+ };
277
+ }
278
+ createTask(params) {
279
+ const id = randomUUID();
280
+ const now = new Date().toISOString();
281
+ this.db.prepare(`
282
+ INSERT INTO tasks (id, title, description, priority, assignee, created_by, depends_on, created_at, updated_at)
283
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
284
+ `).run(id, params.title, params.description ?? null, params.priority ?? "normal", params.assignee ?? null, params.created_by, params.depends_on?.length ? JSON.stringify(params.depends_on) : null, now, now);
285
+ return this.getTask(id);
286
+ }
287
+ getTask(id) {
288
+ const row = this.db.prepare("SELECT * FROM tasks WHERE id = ?").get(id);
289
+ return row ? this.rowToTask(row) : null;
290
+ }
291
+ listTasks(opts) {
292
+ let sql = "SELECT * FROM tasks WHERE 1=1";
293
+ const values = [];
294
+ if (opts?.assignee) {
295
+ sql += " AND assignee = ?";
296
+ values.push(opts.assignee);
297
+ }
298
+ if (opts?.status) {
299
+ sql += " AND status = ?";
300
+ values.push(opts.status);
301
+ }
302
+ sql += " ORDER BY CASE priority WHEN 'urgent' THEN 0 WHEN 'high' THEN 1 WHEN 'normal' THEN 2 ELSE 3 END, created_at";
303
+ return this.db.prepare(sql).all(...values).map(r => this.rowToTask(r));
304
+ }
305
+ updateTask(id, params) {
306
+ const existing = this.getTask(id);
307
+ if (!existing)
308
+ throw new Error(`Task "${id}" not found`);
309
+ const sets = ["updated_at = ?"];
310
+ const values = [new Date().toISOString()];
311
+ if (params.status !== undefined) {
312
+ sets.push("status = ?");
313
+ values.push(params.status);
314
+ }
315
+ if (params.assignee !== undefined) {
316
+ sets.push("assignee = ?");
317
+ values.push(params.assignee);
318
+ }
319
+ if (params.result !== undefined) {
320
+ sets.push("result = ?");
321
+ values.push(params.result);
322
+ }
323
+ if (params.priority !== undefined) {
324
+ sets.push("priority = ?");
325
+ values.push(params.priority);
326
+ }
327
+ values.push(id);
328
+ this.db.prepare(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ?`).run(...values);
329
+ return this.getTask(id);
330
+ }
331
+ claimTask(id, assignee) {
332
+ const task = this.getTask(id);
333
+ if (!task)
334
+ throw new Error(`Task "${id}" not found`);
335
+ if (task.status !== "open")
336
+ throw new Error(`Task "${id}" is ${task.status}, cannot claim`);
337
+ // Check dependencies
338
+ if (task.depends_on.length > 0) {
339
+ for (const depId of task.depends_on) {
340
+ const dep = this.getTask(depId);
341
+ if (dep && dep.status !== "done") {
342
+ throw new Error(`Task "${id}" is blocked by "${depId}" (status: ${dep?.status ?? "not found"})`);
343
+ }
344
+ }
345
+ }
346
+ return this.updateTask(id, { status: "claimed", assignee });
347
+ }
348
+ completeTask(id, result) {
349
+ const task = this.getTask(id);
350
+ if (!task)
351
+ throw new Error(`Task "${id}" not found`);
352
+ if (task.status !== "claimed")
353
+ throw new Error(`Task "${id}" is ${task.status}, cannot complete (must be claimed first)`);
354
+ return this.updateTask(id, { status: "done", result: result ?? undefined });
355
+ }
356
+ close() {
357
+ this.db.close();
358
+ }
359
+ }
360
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/scheduler/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,92 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ import { SchedulerDb } from "./db.js";
6
+ describe("SchedulerDb", () => {
7
+ let dir;
8
+ let db;
9
+ beforeEach(() => {
10
+ dir = mkdtempSync(join(tmpdir(), "scheduler-test-"));
11
+ db = new SchedulerDb(join(dir, "scheduler.db"));
12
+ });
13
+ afterEach(() => {
14
+ db.close();
15
+ rmSync(dir, { recursive: true, force: true });
16
+ });
17
+ it("creates tables on init", () => {
18
+ const schedules = db.list();
19
+ expect(schedules).toEqual([]);
20
+ });
21
+ it("creates and retrieves a schedule", () => {
22
+ const s = db.create({
23
+ cron: "0 7 * * *",
24
+ message: "test message",
25
+ source: "proj-a",
26
+ target: "proj-a",
27
+ reply_chat_id: "-100123",
28
+ reply_thread_id: "42",
29
+ label: "daily test",
30
+ timezone: "Asia/Taipei",
31
+ });
32
+ expect(s.id).toBeTruthy();
33
+ expect(s.cron).toBe("0 7 * * *");
34
+ expect(s.enabled).toBe(true);
35
+ const fetched = db.get(s.id);
36
+ expect(fetched).toEqual(s);
37
+ });
38
+ it("lists schedules with optional target filter", () => {
39
+ db.create({ cron: "0 7 * * *", message: "a", source: "a", target: "a", reply_chat_id: "1", reply_thread_id: null });
40
+ db.create({ cron: "0 8 * * *", message: "b", source: "a", target: "b", reply_chat_id: "1", reply_thread_id: null });
41
+ expect(db.list()).toHaveLength(2);
42
+ expect(db.list("a")).toHaveLength(1);
43
+ expect(db.list("b")).toHaveLength(1);
44
+ });
45
+ it("updates a schedule", () => {
46
+ const s = db.create({ cron: "0 7 * * *", message: "old", source: "a", target: "a", reply_chat_id: "1", reply_thread_id: null });
47
+ const updated = db.update(s.id, { message: "new", enabled: false });
48
+ expect(updated.message).toBe("new");
49
+ expect(updated.enabled).toBe(false);
50
+ expect(updated.cron).toBe("0 7 * * *");
51
+ });
52
+ it("deletes a schedule and cascades runs", () => {
53
+ const s = db.create({ cron: "0 7 * * *", message: "x", source: "a", target: "a", reply_chat_id: "1", reply_thread_id: null });
54
+ db.recordRun(s.id, "delivered");
55
+ expect(db.getRuns(s.id)).toHaveLength(1);
56
+ db.delete(s.id);
57
+ expect(db.get(s.id)).toBeNull();
58
+ expect(db.getRuns(s.id)).toHaveLength(0);
59
+ });
60
+ it("deleteByInstanceOrThread removes matching schedules", () => {
61
+ db.create({ cron: "0 7 * * *", message: "a", source: "a", target: "proj-a", reply_chat_id: "1", reply_thread_id: "42" });
62
+ db.create({ cron: "0 8 * * *", message: "b", source: "b", target: "proj-b", reply_chat_id: "1", reply_thread_id: "42" });
63
+ db.create({ cron: "0 9 * * *", message: "c", source: "c", target: "proj-c", reply_chat_id: "1", reply_thread_id: "99" });
64
+ const count = db.deleteByInstanceOrThread("proj-a", "42");
65
+ expect(count).toBe(2);
66
+ expect(db.list()).toHaveLength(1);
67
+ });
68
+ it("records and retrieves runs", () => {
69
+ const s = db.create({ cron: "0 7 * * *", message: "x", source: "a", target: "a", reply_chat_id: "1", reply_thread_id: null });
70
+ db.recordRun(s.id, "delivered");
71
+ db.recordRun(s.id, "instance_offline", "retry 3x failed");
72
+ const runs = db.getRuns(s.id);
73
+ expect(runs).toHaveLength(2);
74
+ expect(runs[0].status).toBe("instance_offline");
75
+ expect(runs[0].detail).toBe("retry 3x failed");
76
+ });
77
+ it("enforces max schedule count", () => {
78
+ for (let i = 0; i < 5; i++) {
79
+ db.create({ cron: "0 7 * * *", message: `m${i}`, source: "a", target: "a", reply_chat_id: "1", reply_thread_id: null });
80
+ }
81
+ expect(() => db.create({ cron: "0 7 * * *", message: "over", source: "a", target: "a", reply_chat_id: "1", reply_thread_id: null }, 5)).toThrow(/limit/i);
82
+ });
83
+ it("prunes old runs on init", () => {
84
+ const s = db.create({ cron: "0 7 * * *", message: "x", source: "a", target: "a", reply_chat_id: "1", reply_thread_id: null });
85
+ db["db"].prepare("INSERT INTO schedule_runs (schedule_id, triggered_at, status) VALUES (?, datetime('now', '-60 days'), 'delivered')").run(s.id);
86
+ db.recordRun(s.id, "delivered");
87
+ db.pruneOldRuns();
88
+ const runs = db.getRuns(s.id);
89
+ expect(runs).toHaveLength(1);
90
+ });
91
+ });
92
+ //# sourceMappingURL=db.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.test.js","sourceRoot":"","sources":["../../src/scheduler/db.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { Scheduler } from "./scheduler.js";
2
+ export { SchedulerDb } from "./db.js";
3
+ export type { Schedule, ScheduleRun, CreateScheduleParams, UpdateScheduleParams, SchedulerConfig } from "./types.js";
4
+ export { DEFAULT_SCHEDULER_CONFIG } from "./types.js";
@@ -0,0 +1,4 @@
1
+ export { Scheduler } from "./scheduler.js";
2
+ export { SchedulerDb } from "./db.js";
3
+ export { DEFAULT_SCHEDULER_CONFIG } from "./types.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scheduler/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC"}