patchwork-os 0.2.0-alpha.2 → 0.2.0-alpha.21

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 (154) hide show
  1. package/README.bridge.md +6 -0
  2. package/README.md +13 -2
  3. package/dist/approvalHttp.d.ts +11 -2
  4. package/dist/approvalHttp.js +92 -9
  5. package/dist/approvalHttp.js.map +1 -1
  6. package/dist/approvalQueue.d.ts +12 -1
  7. package/dist/approvalQueue.js +25 -3
  8. package/dist/approvalQueue.js.map +1 -1
  9. package/dist/bridge.js +127 -23
  10. package/dist/bridge.js.map +1 -1
  11. package/dist/claudeDriver.d.ts +3 -1
  12. package/dist/claudeDriver.js +48 -0
  13. package/dist/claudeDriver.js.map +1 -1
  14. package/dist/claudeOrchestrator.d.ts +1 -1
  15. package/dist/claudeOrchestrator.js +14 -8
  16. package/dist/claudeOrchestrator.js.map +1 -1
  17. package/dist/commands/launchd.d.ts +2 -0
  18. package/dist/commands/launchd.js +94 -0
  19. package/dist/commands/launchd.js.map +1 -0
  20. package/dist/config.d.ts +7 -2
  21. package/dist/config.js +85 -8
  22. package/dist/config.js.map +1 -1
  23. package/dist/connectors/github.d.ts +58 -8
  24. package/dist/connectors/github.js +321 -84
  25. package/dist/connectors/github.js.map +1 -1
  26. package/dist/connectors/gmail.d.ts +4 -1
  27. package/dist/connectors/gmail.js +77 -16
  28. package/dist/connectors/gmail.js.map +1 -1
  29. package/dist/connectors/googleCalendar.d.ts +60 -0
  30. package/dist/connectors/googleCalendar.js +329 -0
  31. package/dist/connectors/googleCalendar.js.map +1 -0
  32. package/dist/connectors/linear.d.ts +117 -0
  33. package/dist/connectors/linear.js +248 -0
  34. package/dist/connectors/linear.js.map +1 -0
  35. package/dist/connectors/mcpClient.d.ts +56 -0
  36. package/dist/connectors/mcpClient.js +189 -0
  37. package/dist/connectors/mcpClient.js.map +1 -0
  38. package/dist/connectors/mcpOAuth.d.ts +83 -0
  39. package/dist/connectors/mcpOAuth.js +363 -0
  40. package/dist/connectors/mcpOAuth.js.map +1 -0
  41. package/dist/connectors/sentry.d.ts +43 -0
  42. package/dist/connectors/sentry.js +197 -0
  43. package/dist/connectors/sentry.js.map +1 -0
  44. package/dist/connectors/slack.d.ts +50 -0
  45. package/dist/connectors/slack.js +289 -0
  46. package/dist/connectors/slack.js.map +1 -0
  47. package/dist/drivers/claude/api.d.ts +11 -0
  48. package/dist/drivers/claude/api.js +54 -0
  49. package/dist/drivers/claude/api.js.map +1 -0
  50. package/dist/drivers/claude/envSanitizer.d.ts +7 -0
  51. package/dist/drivers/claude/envSanitizer.js +18 -0
  52. package/dist/drivers/claude/envSanitizer.js.map +1 -0
  53. package/dist/drivers/claude/streamParser.d.ts +38 -0
  54. package/dist/drivers/claude/streamParser.js +34 -0
  55. package/dist/drivers/claude/streamParser.js.map +1 -0
  56. package/dist/drivers/claude/subprocess.d.ts +19 -0
  57. package/dist/drivers/claude/subprocess.js +216 -0
  58. package/dist/drivers/claude/subprocess.js.map +1 -0
  59. package/dist/drivers/claude/subprocessSettings.d.ts +9 -0
  60. package/dist/drivers/claude/subprocessSettings.js +55 -0
  61. package/dist/drivers/claude/subprocessSettings.js.map +1 -0
  62. package/dist/drivers/gemini/index.d.ts +18 -0
  63. package/dist/drivers/gemini/index.js +210 -0
  64. package/dist/drivers/gemini/index.js.map +1 -0
  65. package/dist/drivers/grok/index.d.ts +11 -0
  66. package/dist/drivers/grok/index.js +22 -0
  67. package/dist/drivers/grok/index.js.map +1 -0
  68. package/dist/drivers/index.d.ts +23 -0
  69. package/dist/drivers/index.js +31 -0
  70. package/dist/drivers/index.js.map +1 -0
  71. package/dist/drivers/openai/index.d.ts +24 -0
  72. package/dist/drivers/openai/index.js +110 -0
  73. package/dist/drivers/openai/index.js.map +1 -0
  74. package/dist/drivers/types.d.ts +72 -0
  75. package/dist/drivers/types.js +30 -0
  76. package/dist/drivers/types.js.map +1 -0
  77. package/dist/index.js +35 -1
  78. package/dist/index.js.map +1 -1
  79. package/dist/installGuard.d.ts +25 -0
  80. package/dist/installGuard.js +48 -0
  81. package/dist/installGuard.js.map +1 -0
  82. package/dist/patchworkConfig.d.ts +9 -0
  83. package/dist/patchworkConfig.js.map +1 -1
  84. package/dist/recipes/scheduler.d.ts +23 -7
  85. package/dist/recipes/scheduler.js +135 -41
  86. package/dist/recipes/scheduler.js.map +1 -1
  87. package/dist/recipes/yamlRunner.d.ts +15 -0
  88. package/dist/recipes/yamlRunner.js +325 -26
  89. package/dist/recipes/yamlRunner.js.map +1 -1
  90. package/dist/recipesHttp.d.ts +14 -1
  91. package/dist/recipesHttp.js +21 -4
  92. package/dist/recipesHttp.js.map +1 -1
  93. package/dist/runLog.d.ts +5 -0
  94. package/dist/runLog.js +51 -1
  95. package/dist/runLog.js.map +1 -1
  96. package/dist/server.d.ts +15 -1
  97. package/dist/server.js +458 -31
  98. package/dist/server.js.map +1 -1
  99. package/dist/tools/addLinearComment.d.ts +55 -0
  100. package/dist/tools/addLinearComment.js +72 -0
  101. package/dist/tools/addLinearComment.js.map +1 -0
  102. package/dist/tools/bridgeDoctor.js +2 -2
  103. package/dist/tools/bridgeDoctor.js.map +1 -1
  104. package/dist/tools/createLinearIssue.d.ts +84 -0
  105. package/dist/tools/createLinearIssue.js +146 -0
  106. package/dist/tools/createLinearIssue.js.map +1 -0
  107. package/dist/tools/ctxGetTaskContext.d.ts +4 -1
  108. package/dist/tools/ctxGetTaskContext.js +45 -2
  109. package/dist/tools/ctxGetTaskContext.js.map +1 -1
  110. package/dist/tools/fetchCalendarEvents.d.ts +94 -0
  111. package/dist/tools/fetchCalendarEvents.js +97 -0
  112. package/dist/tools/fetchCalendarEvents.js.map +1 -0
  113. package/dist/tools/fetchGithubIssue.d.ts +80 -0
  114. package/dist/tools/fetchGithubIssue.js +84 -0
  115. package/dist/tools/fetchGithubIssue.js.map +1 -0
  116. package/dist/tools/fetchGithubPR.d.ts +89 -0
  117. package/dist/tools/fetchGithubPR.js +96 -0
  118. package/dist/tools/fetchGithubPR.js.map +1 -0
  119. package/dist/tools/fetchLinearIssue.d.ts +112 -0
  120. package/dist/tools/fetchLinearIssue.js +129 -0
  121. package/dist/tools/fetchLinearIssue.js.map +1 -0
  122. package/dist/tools/fetchSentryIssue.d.ts +143 -0
  123. package/dist/tools/fetchSentryIssue.js +150 -0
  124. package/dist/tools/fetchSentryIssue.js.map +1 -0
  125. package/dist/tools/fetchSlackProfile.d.ts +43 -0
  126. package/dist/tools/fetchSlackProfile.js +46 -0
  127. package/dist/tools/fetchSlackProfile.js.map +1 -0
  128. package/dist/tools/getConnectorStatus.d.ts +58 -0
  129. package/dist/tools/getConnectorStatus.js +56 -0
  130. package/dist/tools/getConnectorStatus.js.map +1 -0
  131. package/dist/tools/github/index.d.ts +1 -1
  132. package/dist/tools/github/index.js +1 -1
  133. package/dist/tools/github/index.js.map +1 -1
  134. package/dist/tools/github/pr.d.ts +122 -0
  135. package/dist/tools/github/pr.js +183 -0
  136. package/dist/tools/github/pr.js.map +1 -1
  137. package/dist/tools/index.js +27 -1
  138. package/dist/tools/index.js.map +1 -1
  139. package/dist/tools/slackListChannels.d.ts +65 -0
  140. package/dist/tools/slackListChannels.js +70 -0
  141. package/dist/tools/slackListChannels.js.map +1 -0
  142. package/dist/tools/slackPostMessage.d.ts +57 -0
  143. package/dist/tools/slackPostMessage.js +77 -0
  144. package/dist/tools/slackPostMessage.js.map +1 -0
  145. package/dist/tools/updateLinearIssue.d.ts +89 -0
  146. package/dist/tools/updateLinearIssue.js +117 -0
  147. package/dist/tools/updateLinearIssue.js.map +1 -0
  148. package/package.json +4 -2
  149. package/scripts/start-all.sh +56 -19
  150. package/templates/co.patchwork-os.bridge.plist +34 -0
  151. package/templates/recipes/ctx-loop-test.yaml +75 -0
  152. package/templates/recipes/morning-brief-slack.yaml +57 -0
  153. package/templates/recipes/morning-brief.yaml +21 -5
  154. package/templates/recipes/sentry-to-linear.yaml +77 -0
@@ -0,0 +1,48 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { lstatSync, realpathSync } from "node:fs";
3
+ import path from "node:path";
4
+ export const PATCHWORK_PACKAGE_NAME = "patchwork-os";
5
+ /**
6
+ * Detects a symlinked global install produced by `npm install -g .` from a
7
+ * workspace checkout.
8
+ *
9
+ * Strategy: ask npm for the global node_modules root, then lstat the
10
+ * patchwork-os slot inside it. If the slot is a symlink, the install is
11
+ * workspace-linked rather than a real copy.
12
+ *
13
+ * We cannot reliably use import.meta.url or process.argv[1] for this check:
14
+ * - import.meta.url is resolved by Node before we read it (real path, not logical)
15
+ * - process.argv[1] points to the bin shim (/opt/homebrew/bin/patchwork-os),
16
+ * not to the package root in node_modules
17
+ *
18
+ * Returns null for normal installs or when the check cannot be performed.
19
+ */
20
+ export function detectWorkspaceSymlinkInstall() {
21
+ try {
22
+ // Get the global node_modules root from npm.
23
+ const result = spawnSync("npm", ["root", "-g"], {
24
+ encoding: "utf-8",
25
+ timeout: 5000,
26
+ });
27
+ if (result.error || result.status !== 0)
28
+ return null;
29
+ const globalRoot = result.stdout.trim();
30
+ if (!globalRoot)
31
+ return null;
32
+ const logicalRoot = path.join(globalRoot, PATCHWORK_PACKAGE_NAME);
33
+ // Check if the slot is a symlink (not a real directory copy).
34
+ const stat = lstatSync(logicalRoot);
35
+ if (!stat.isSymbolicLink())
36
+ return null;
37
+ const realRoot = realpathSync(logicalRoot);
38
+ return { logicalRoot, realRoot };
39
+ }
40
+ catch {
41
+ // npm not found, permission error, etc. → safe default.
42
+ return null;
43
+ }
44
+ }
45
+ /** Human-readable install fix instructions. */
46
+ export const SYMLINK_INSTALL_FIX = ` Fix: npm pack && npm install -g ${PATCHWORK_PACKAGE_NAME}-*.tgz\n` +
47
+ ` Or install from the registry: npm install -g ${PATCHWORK_PACKAGE_NAME}\n`;
48
+ //# sourceMappingURL=installGuard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installGuard.js","sourceRoot":"","sources":["../src/installGuard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,sBAAsB,GAAG,cAAc,CAAC;AASrD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,6BAA6B;IAC3C,IAAI,CAAC;QACH,6CAA6C;QAC7C,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE;YAC9C,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAErD,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QAE7B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;QAElE,8DAA8D;QAC9D,MAAM,IAAI,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE;YAAE,OAAO,IAAI,CAAC;QAExC,MAAM,QAAQ,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;QAC3C,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,wDAAwD;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,+CAA+C;AAC/C,MAAM,CAAC,MAAM,mBAAmB,GAC9B,qCAAqC,sBAAsB,UAAU;IACrE,kDAAkD,sBAAsB,IAAI,CAAC"}
@@ -21,6 +21,15 @@ export interface PatchworkConfig {
21
21
  approvalGate?: "off" | "high" | "all";
22
22
  /** Absolute path to a managed settings file (admin-controlled, highest rule precedence). */
23
23
  managedSettingsPath?: string;
24
+ recipes?: {
25
+ disabled?: string[];
26
+ };
27
+ /** AI driver mode — persisted so dashboard changes survive restart. */
28
+ driver?: "subprocess" | "api" | "openai" | "grok" | "gemini" | "none";
29
+ /** Notification channel config */
30
+ notifications?: {
31
+ slackChannel?: string;
32
+ };
24
33
  }
25
34
  export declare function defaultConfigPath(): string;
26
35
  export declare function loadConfig(path?: string): PatchworkConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"patchworkConfig.js","sourceRoot":"","sources":["../src/patchworkConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA2B1C,MAAM,QAAQ,GAAoB;IAChC,KAAK,EAAE,QAAQ;IACf,SAAS,EAAE;QACT,IAAI,EAAE,IAAI;QACV,eAAe,EAAE,CAAC,MAAM,CAAC;QACzB,iBAAiB,EAAE,KAAK;KACzB;IACD,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,SAAS,CAAC;CACrD,CAAC;AAEF,MAAM,UAAU,iBAAiB;IAC/B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAI,GAAG,iBAAiB,EAAE;IACnD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IAC9C,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA6B,CAAC;IAC3D,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,MAAuB,EACvB,IAAI,GAAG,iBAAiB,EAAE;IAE1B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACzE,CAAC"}
1
+ {"version":3,"file":"patchworkConfig.js","sourceRoot":"","sources":["../src/patchworkConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAoC1C,MAAM,QAAQ,GAAoB;IAChC,KAAK,EAAE,QAAQ;IACf,SAAS,EAAE;QACT,IAAI,EAAE,IAAI;QACV,eAAe,EAAE,CAAC,MAAM,CAAC;QACzB,iBAAiB,EAAE,KAAK;KACzB;IACD,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,SAAS,CAAC;CACrD,CAAC;AAEF,MAAM,UAAU,iBAAiB;IAC/B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAI,GAAG,iBAAiB,EAAE;IACnD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IAC9C,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA6B,CAAC;IAC3D,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,MAAuB,EACvB,IAAI,GAAG,iBAAiB,EAAE;IAE1B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACzE,CAAC"}
@@ -1,10 +1,12 @@
1
+ import cron from "node-cron";
1
2
  import type { Logger } from "../logger.js";
2
3
  /**
3
- * RecipeScheduler — runs cron-triggered recipes on a simple interval.
4
+ * RecipeScheduler — runs cron-triggered recipes on a simple interval or
5
+ * standard 5-field cron expression.
4
6
  *
5
- * Phase-0 scope supports the `@every Ns|Nm|Nh` schedule form, which is
6
- * enough to demonstrate "works while you're away" without pulling in a full
7
- * cron dependency. Standard 5-field expressions can be added later.
7
+ * Supported schedule forms:
8
+ * @every Ns|Nm|Nh — simple interval (setInterval-based)
9
+ * <5-field cron> — standard cron expression (node-cron-based)
8
10
  *
9
11
  * Scheduler is a pure consumer of the recipes-on-disk contract and an
10
12
  * injected enqueue fn, so it's trivial to unit test without the orchestrator.
@@ -13,15 +15,20 @@ export type SchedulerEnqueue = (opts: {
13
15
  prompt: string;
14
16
  triggerSource: string;
15
17
  }) => string;
18
+ export type SchedulerRunYaml = (name: string) => Promise<void>;
16
19
  export interface ScheduledRecipe {
17
20
  name: string;
18
21
  schedule: string;
19
22
  intervalMs: number;
20
23
  timer: ReturnType<typeof setInterval>;
24
+ /** Present only for cron5-kind recipes. */
25
+ cronJob?: cron.ScheduledTask;
21
26
  }
22
27
  export interface SchedulerOptions {
23
28
  recipesDir: string;
24
29
  enqueue: SchedulerEnqueue;
30
+ /** Called for YAML recipes instead of enqueue. */
31
+ runYaml?: SchedulerRunYaml;
25
32
  logger?: Logger;
26
33
  /** Override for tests — defaults to setInterval. */
27
34
  setInterval?: typeof setInterval;
@@ -36,10 +43,19 @@ export declare class RecipeScheduler {
36
43
  constructor(opts: SchedulerOptions);
37
44
  start(): ScheduledRecipe[];
38
45
  stop(): void;
39
- list(): ReadonlyArray<Omit<ScheduledRecipe, "timer">>;
46
+ restart(): void;
47
+ list(): ReadonlyArray<Omit<ScheduledRecipe, "timer" | "cronJob">>;
40
48
  /** Test hook: dispatch a recipe immediately without waiting for the interval. */
41
49
  fireForTest(name: string): void;
42
50
  private fire;
43
51
  }
44
- /** Parse @every forms into milliseconds. Returns null for unsupported schedules. */
45
- export declare function parseSchedule(schedule: string): number | null;
52
+ type ParsedSchedule = {
53
+ kind: "interval";
54
+ intervalMs: number;
55
+ } | {
56
+ kind: "cron5";
57
+ expression: string;
58
+ };
59
+ /** Parse @every forms into milliseconds, or detect a 5-field cron expression. Returns null for unsupported schedules. */
60
+ export declare function parseSchedule(schedule: string): ParsedSchedule | null;
61
+ export {};
@@ -1,5 +1,8 @@
1
- import { readdirSync, readFileSync } from "node:fs";
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
2
  import path from "node:path";
3
+ import cron from "node-cron";
4
+ import { parse as parseYaml } from "yaml";
5
+ import { loadConfig } from "../patchworkConfig.js";
3
6
  import { loadRecipePrompt } from "../recipesHttp.js";
4
7
  export class RecipeScheduler {
5
8
  opts;
@@ -13,6 +16,17 @@ export class RecipeScheduler {
13
16
  }
14
17
  start() {
15
18
  this.stop();
19
+ // Load disabled list from config
20
+ let disabled = new Set();
21
+ try {
22
+ const cfg = loadConfig();
23
+ if (cfg.recipes?.disabled) {
24
+ disabled = new Set(cfg.recipes.disabled);
25
+ }
26
+ }
27
+ catch {
28
+ // non-fatal — proceed with empty disabled set
29
+ }
16
30
  let entries;
17
31
  try {
18
32
  entries = readdirSync(this.opts.recipesDir);
@@ -21,35 +35,80 @@ export class RecipeScheduler {
21
35
  return [];
22
36
  }
23
37
  for (const f of entries) {
24
- if (!f.endsWith(".json") || f.endsWith(".permissions.json"))
38
+ const isJson = f.endsWith(".json") && !f.endsWith(".permissions.json");
39
+ const isYaml = f.endsWith(".yaml") || f.endsWith(".yml");
40
+ if (!isJson && !isYaml)
25
41
  continue;
26
42
  const fullPath = path.join(this.opts.recipesDir, f);
27
43
  try {
28
- const raw = readFileSync(fullPath, "utf-8");
29
- const parsed = JSON.parse(raw);
30
- if (parsed.trigger?.type !== "cron")
31
- continue;
32
- if (!parsed.trigger.schedule ||
33
- typeof parsed.trigger.schedule !== "string")
44
+ let name;
45
+ let schedule;
46
+ if (isJson) {
47
+ const raw = readFileSync(fullPath, "utf-8");
48
+ const parsed = JSON.parse(raw);
49
+ if (parsed.trigger?.type !== "cron")
50
+ continue;
51
+ if (!parsed.trigger.schedule ||
52
+ typeof parsed.trigger.schedule !== "string")
53
+ continue;
54
+ schedule = parsed.trigger.schedule;
55
+ name = parsed.name ?? path.basename(f, ".json");
56
+ }
57
+ else {
58
+ // YAML
59
+ const raw = readFileSync(fullPath, "utf-8");
60
+ const parsed = parseYaml(raw);
61
+ if (parsed.trigger?.type !== "cron")
62
+ continue;
63
+ schedule = parsed.trigger.at ?? parsed.trigger.schedule;
64
+ if (!schedule || typeof schedule !== "string")
65
+ continue;
66
+ name =
67
+ parsed.name ?? path.basename(f, isYaml ? path.extname(f) : ".yaml");
68
+ }
69
+ // Apply disabled filter
70
+ if (disabled.has(name)) {
71
+ this.opts.logger?.info?.(`[scheduler] skipping disabled recipe "${name}"`);
34
72
  continue;
35
- const intervalMs = parseSchedule(parsed.trigger.schedule);
36
- if (intervalMs === null) {
37
- this.opts.logger?.warn?.(`[scheduler] ignoring recipe "${parsed.name ?? f}" — unsupported schedule "${parsed.trigger.schedule}" (use @every Ns|Nm|Nh)`);
73
+ }
74
+ const parsed2 = parseSchedule(schedule);
75
+ if (parsed2 === null) {
76
+ this.opts.logger?.warn?.(`[scheduler] ignoring recipe "${name}" — unsupported schedule "${schedule}" (use @every Ns|Nm|Nh or a 5-field cron expression)`);
38
77
  continue;
39
78
  }
40
- const name = parsed.name ?? path.basename(f, ".json");
41
- const timer = this.setIntervalFn(() => {
42
- this.fire(name);
43
- }, intervalMs);
44
- if (typeof timer === "object" && "unref" in timer)
45
- timer.unref();
46
- this.scheduled.push({
47
- name,
48
- schedule: parsed.trigger.schedule,
49
- intervalMs,
50
- timer,
51
- });
52
- this.opts.logger?.info?.(`[scheduler] "${name}" scheduled every ${intervalMs}ms (${parsed.trigger.schedule})`);
79
+ if (parsed2.kind === "interval") {
80
+ const intervalMs = parsed2.intervalMs;
81
+ const timer = this.setIntervalFn(() => {
82
+ this.fire(name);
83
+ }, intervalMs);
84
+ if (typeof timer === "object" && "unref" in timer)
85
+ timer.unref();
86
+ this.scheduled.push({
87
+ name,
88
+ schedule,
89
+ intervalMs,
90
+ timer,
91
+ });
92
+ this.opts.logger?.info?.(`[scheduler] "${name}" scheduled every ${intervalMs}ms (${schedule})`);
93
+ }
94
+ else {
95
+ // cron5
96
+ const cronJob = cron.schedule(parsed2.expression, () => {
97
+ this.fire(name);
98
+ });
99
+ // Store a sentinel timer so the ScheduledRecipe shape stays stable
100
+ const dummyTimer = this.setIntervalFn(() => { }, 2_147_483_647);
101
+ if (typeof dummyTimer === "object" && "unref" in dummyTimer)
102
+ dummyTimer.unref();
103
+ this.scheduled.push({
104
+ name,
105
+ schedule,
106
+ intervalMs: 0,
107
+ timer: dummyTimer,
108
+ cronJob,
109
+ });
110
+ this.opts.logger?.info?.(`[scheduler] "${name}" scheduled with cron expression "${schedule}"`);
111
+ }
53
112
  }
54
113
  catch {
55
114
  // skip malformed recipe
@@ -59,18 +118,45 @@ export class RecipeScheduler {
59
118
  }
60
119
  stop() {
61
120
  for (const entry of this.scheduled) {
62
- this.clearIntervalFn(entry.timer);
121
+ if (entry.cronJob) {
122
+ entry.cronJob.stop();
123
+ }
124
+ else {
125
+ this.clearIntervalFn(entry.timer);
126
+ }
63
127
  }
64
128
  this.scheduled = [];
65
129
  }
130
+ restart() {
131
+ this.stop();
132
+ this.start();
133
+ }
66
134
  list() {
67
- return this.scheduled.map(({ timer: _t, ...rest }) => rest);
135
+ return this.scheduled.map(({ timer: _t, cronJob: _c, ...rest }) => rest);
68
136
  }
69
137
  /** Test hook: dispatch a recipe immediately without waiting for the interval. */
70
138
  fireForTest(name) {
71
139
  this.fire(name);
72
140
  }
73
141
  fire(name) {
142
+ // YAML recipe — delegate to runYaml if provided
143
+ const yamlPath = existsSync(path.join(this.opts.recipesDir, `${name}.yaml`))
144
+ ? path.join(this.opts.recipesDir, `${name}.yaml`)
145
+ : existsSync(path.join(this.opts.recipesDir, `${name}.yml`))
146
+ ? path.join(this.opts.recipesDir, `${name}.yml`)
147
+ : null;
148
+ if (yamlPath) {
149
+ if (!this.opts.runYaml) {
150
+ this.opts.logger?.warn?.(`[scheduler] skipped "${name}" — YAML recipe requires runYaml callback (start bridge with --claude-driver)`);
151
+ return;
152
+ }
153
+ this.opts.runYaml(name).catch((err) => {
154
+ this.opts.logger?.warn?.(`[scheduler] YAML recipe "${name}" failed: ${err instanceof Error ? err.message : String(err)}`);
155
+ });
156
+ this.opts.logger?.info?.(`[scheduler] fired YAML recipe "${name}"`);
157
+ return;
158
+ }
159
+ // JSON recipe — legacy path
74
160
  const loaded = loadRecipePrompt(this.opts.recipesDir, name);
75
161
  if (!loaded) {
76
162
  this.opts.logger?.warn?.(`[scheduler] skipped "${name}" — recipe file disappeared`);
@@ -88,23 +174,31 @@ export class RecipeScheduler {
88
174
  }
89
175
  }
90
176
  }
91
- /** Parse @every forms into milliseconds. Returns null for unsupported schedules. */
177
+ /** Parse @every forms into milliseconds, or detect a 5-field cron expression. Returns null for unsupported schedules. */
92
178
  export function parseSchedule(schedule) {
93
179
  const trimmed = schedule.trim();
180
+ // @every Ns|Nm|Nh
94
181
  const m = /^@every\s+(\d+)\s*(ms|s|m|h)$/i.exec(trimmed);
95
- if (!m)
96
- return null;
97
- const n = Number.parseInt(m[1], 10);
98
- const unit = m[2].toLowerCase();
99
- if (!Number.isFinite(n) || n <= 0)
100
- return null;
101
- const multiplier = unit === "ms"
102
- ? 1
103
- : unit === "s"
104
- ? 1000
105
- : unit === "m"
106
- ? 60_000
107
- : 60 * 60_000;
108
- return n * multiplier;
182
+ if (m) {
183
+ const n = Number.parseInt(m[1], 10);
184
+ const unit = m[2]?.toLowerCase();
185
+ if (!Number.isFinite(n) || n <= 0)
186
+ return null;
187
+ const multiplier = unit === "ms"
188
+ ? 1
189
+ : unit === "s"
190
+ ? 1000
191
+ : unit === "m"
192
+ ? 60_000
193
+ : 60 * 60_000;
194
+ return { kind: "interval", intervalMs: n * multiplier };
195
+ }
196
+ // 5-field cron expression (e.g. "0 8 * * 1-5")
197
+ if (/^\S+\s+\S+\s+\S+\s+\S+\s+\S+$/.test(trimmed)) {
198
+ if (cron.validate(trimmed)) {
199
+ return { kind: "cron5", expression: trimmed };
200
+ }
201
+ }
202
+ return null;
109
203
  }
110
204
  //# sourceMappingURL=scheduler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/recipes/scheduler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/recipes/scheduler.ts"],"names":[],"mappings":"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"}
@@ -75,12 +75,27 @@ export interface RunnerDeps {
75
75
  claudeFn?: (prompt: string, model: string) => Promise<string>;
76
76
  /** Optional Claude Code CLI caller for agent steps with driver: claude-code. */
77
77
  claudeCodeFn?: (prompt: string) => Promise<string>;
78
+ /**
79
+ * Optional provider driver invoker for agent steps with driver: openai|grok|gemini.
80
+ * Dispatches to src/drivers/* under the hood. If not provided, the runner will
81
+ * lazily construct a driver via createDriver() from drivers/index.js.
82
+ */
83
+ providerDriverFn?: (driverName: "openai" | "grok" | "gemini", prompt: string, model: string | undefined) => Promise<string>;
84
+ }
85
+ export interface StepResult {
86
+ id: string;
87
+ tool?: string;
88
+ status: "ok" | "skipped" | "error";
89
+ error?: string;
90
+ durationMs: number;
78
91
  }
79
92
  export interface RunResult {
80
93
  recipe: string;
81
94
  stepsRun: number;
82
95
  outputs: string[];
83
96
  context: RunContext;
97
+ stepResults: StepResult[];
98
+ errorMessage?: string;
84
99
  }
85
100
  export declare function loadYamlRecipe(filePath: string): YamlRecipe;
86
101
  export declare function validateYamlRecipe(raw: unknown): YamlRecipe;