patchwork-os 0.2.0-alpha.3 → 0.2.0-alpha.31
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.
- package/README.bridge.md +6 -0
- package/README.md +40 -15
- package/deploy/bootstrap-vps.sh +184 -0
- package/deploy/deploy-dashboard.sh +174 -0
- package/deploy/deploy-landing.sh +79 -0
- package/dist/activationMetrics.d.ts +67 -0
- package/dist/activationMetrics.js +255 -0
- package/dist/activationMetrics.js.map +1 -0
- package/dist/approvalHttp.d.ts +24 -2
- package/dist/approvalHttp.js +150 -10
- package/dist/approvalHttp.js.map +1 -1
- package/dist/approvalQueue.d.ts +16 -1
- package/dist/approvalQueue.js +44 -3
- package/dist/approvalQueue.js.map +1 -1
- package/dist/automation.d.ts +20 -0
- package/dist/automation.js +54 -1
- package/dist/automation.js.map +1 -1
- package/dist/bridge.d.ts +2 -0
- package/dist/bridge.js +55 -130
- package/dist/bridge.js.map +1 -1
- package/dist/bridgeToken.js +57 -19
- package/dist/bridgeToken.js.map +1 -1
- package/dist/ccPermissions.js +6 -4
- package/dist/ccPermissions.js.map +1 -1
- package/dist/claudeOrchestrator.d.ts +1 -1
- package/dist/claudeOrchestrator.js +14 -8
- package/dist/claudeOrchestrator.js.map +1 -1
- package/dist/commands/launchd.d.ts +2 -0
- package/dist/commands/launchd.js +94 -0
- package/dist/commands/launchd.js.map +1 -0
- package/dist/commands/recipe.d.ts +258 -0
- package/dist/commands/recipe.js +1130 -0
- package/dist/commands/recipe.js.map +1 -0
- package/dist/commands/recipeInstall.d.ts +72 -0
- package/dist/commands/recipeInstall.js +339 -0
- package/dist/commands/recipeInstall.js.map +1 -0
- package/dist/config.d.ts +14 -1
- package/dist/config.js +99 -8
- package/dist/config.js.map +1 -1
- package/dist/connectors/baseConnector.d.ts +117 -0
- package/dist/connectors/baseConnector.js +213 -0
- package/dist/connectors/baseConnector.js.map +1 -0
- package/dist/connectors/confluence.d.ts +111 -0
- package/dist/connectors/confluence.js +406 -0
- package/dist/connectors/confluence.js.map +1 -0
- package/dist/connectors/datadog.d.ts +116 -0
- package/dist/connectors/datadog.js +385 -0
- package/dist/connectors/datadog.js.map +1 -0
- package/dist/connectors/fixtureLibrary.d.ts +21 -0
- package/dist/connectors/fixtureLibrary.js +70 -0
- package/dist/connectors/fixtureLibrary.js.map +1 -0
- package/dist/connectors/fixtureRecorder.d.ts +1 -0
- package/dist/connectors/fixtureRecorder.js +35 -0
- package/dist/connectors/fixtureRecorder.js.map +1 -0
- package/dist/connectors/github.d.ts +58 -8
- package/dist/connectors/github.js +312 -84
- package/dist/connectors/github.js.map +1 -1
- package/dist/connectors/gmail.d.ts +4 -1
- package/dist/connectors/gmail.js +79 -16
- package/dist/connectors/gmail.js.map +1 -1
- package/dist/connectors/googleCalendar.d.ts +60 -0
- package/dist/connectors/googleCalendar.js +345 -0
- package/dist/connectors/googleCalendar.js.map +1 -0
- package/dist/connectors/hubspot.d.ts +112 -0
- package/dist/connectors/hubspot.js +408 -0
- package/dist/connectors/hubspot.js.map +1 -0
- package/dist/connectors/intercom.d.ts +102 -0
- package/dist/connectors/intercom.js +402 -0
- package/dist/connectors/intercom.js.map +1 -0
- package/dist/connectors/jira.d.ts +98 -0
- package/dist/connectors/jira.js +379 -0
- package/dist/connectors/jira.js.map +1 -0
- package/dist/connectors/linear.d.ts +69 -19
- package/dist/connectors/linear.js +170 -129
- package/dist/connectors/linear.js.map +1 -1
- package/dist/connectors/mcpClient.d.ts +56 -0
- package/dist/connectors/mcpClient.js +189 -0
- package/dist/connectors/mcpClient.js.map +1 -0
- package/dist/connectors/mcpOAuth.d.ts +84 -0
- package/dist/connectors/mcpOAuth.js +389 -0
- package/dist/connectors/mcpOAuth.js.map +1 -0
- package/dist/connectors/mockConnector.d.ts +28 -0
- package/dist/connectors/mockConnector.js +81 -0
- package/dist/connectors/mockConnector.js.map +1 -0
- package/dist/connectors/notion.d.ts +143 -0
- package/dist/connectors/notion.js +424 -0
- package/dist/connectors/notion.js.map +1 -0
- package/dist/connectors/sentry.d.ts +17 -21
- package/dist/connectors/sentry.js +115 -131
- package/dist/connectors/sentry.js.map +1 -1
- package/dist/connectors/slack.d.ts +50 -0
- package/dist/connectors/slack.js +324 -0
- package/dist/connectors/slack.js.map +1 -0
- package/dist/connectors/stripe.d.ts +116 -0
- package/dist/connectors/stripe.js +379 -0
- package/dist/connectors/stripe.js.map +1 -0
- package/dist/connectors/tokenStorage.d.ts +35 -0
- package/dist/connectors/tokenStorage.js +459 -0
- package/dist/connectors/tokenStorage.js.map +1 -0
- package/dist/connectors/zendesk.d.ts +104 -0
- package/dist/connectors/zendesk.js +424 -0
- package/dist/connectors/zendesk.js.map +1 -0
- package/dist/drivers/gemini/index.d.ts +5 -1
- package/dist/drivers/gemini/index.js +39 -5
- package/dist/drivers/gemini/index.js.map +1 -1
- package/dist/drivers/index.d.ts +5 -0
- package/dist/drivers/index.js +1 -1
- package/dist/drivers/index.js.map +1 -1
- package/dist/featureFlags.d.ts +73 -0
- package/dist/featureFlags.js +203 -0
- package/dist/featureFlags.js.map +1 -0
- package/dist/fp/automationInterpreter.js +1 -0
- package/dist/fp/automationInterpreter.js.map +1 -1
- package/dist/fp/automationProgram.d.ts +1 -1
- package/dist/fp/automationProgram.js.map +1 -1
- package/dist/fp/policyParser.js +17 -0
- package/dist/fp/policyParser.js.map +1 -1
- package/dist/index.js +621 -61
- package/dist/index.js.map +1 -1
- package/dist/installGuard.d.ts +25 -0
- package/dist/installGuard.js +48 -0
- package/dist/installGuard.js.map +1 -0
- package/dist/oauth.d.ts +4 -1
- package/dist/oauth.js +50 -14
- package/dist/oauth.js.map +1 -1
- package/dist/patchworkConfig.d.ts +9 -0
- package/dist/patchworkConfig.js.map +1 -1
- package/dist/recipeOrchestration.d.ts +53 -0
- package/dist/recipeOrchestration.js +272 -0
- package/dist/recipeOrchestration.js.map +1 -0
- package/dist/recipes/RecipeOrchestrator.d.ts +40 -0
- package/dist/recipes/RecipeOrchestrator.js +51 -0
- package/dist/recipes/RecipeOrchestrator.js.map +1 -0
- package/dist/recipes/agentExecutor.d.ts +28 -0
- package/dist/recipes/agentExecutor.js +42 -0
- package/dist/recipes/agentExecutor.js.map +1 -0
- package/dist/recipes/chainedRunner.d.ts +140 -0
- package/dist/recipes/chainedRunner.js +539 -0
- package/dist/recipes/chainedRunner.js.map +1 -0
- package/dist/recipes/dependencyGraph.d.ts +39 -0
- package/dist/recipes/dependencyGraph.js +199 -0
- package/dist/recipes/dependencyGraph.js.map +1 -0
- package/dist/recipes/legacyRecipeCompat.d.ts +2 -0
- package/dist/recipes/legacyRecipeCompat.js +112 -0
- package/dist/recipes/legacyRecipeCompat.js.map +1 -0
- package/dist/recipes/manifest.d.ts +47 -0
- package/dist/recipes/manifest.js +141 -0
- package/dist/recipes/manifest.js.map +1 -0
- package/dist/recipes/nestedRecipeStep.d.ts +58 -0
- package/dist/recipes/nestedRecipeStep.js +95 -0
- package/dist/recipes/nestedRecipeStep.js.map +1 -0
- package/dist/recipes/outputRegistry.d.ts +28 -0
- package/dist/recipes/outputRegistry.js +52 -0
- package/dist/recipes/outputRegistry.js.map +1 -0
- package/dist/recipes/scheduler.d.ts +23 -7
- package/dist/recipes/scheduler.js +131 -41
- package/dist/recipes/scheduler.js.map +1 -1
- package/dist/recipes/schema.d.ts +17 -2
- package/dist/recipes/schemaGenerator.d.ts +28 -0
- package/dist/recipes/schemaGenerator.js +565 -0
- package/dist/recipes/schemaGenerator.js.map +1 -0
- package/dist/recipes/templateEngine.d.ts +62 -0
- package/dist/recipes/templateEngine.js +182 -0
- package/dist/recipes/templateEngine.js.map +1 -0
- package/dist/recipes/toolRegistry.d.ts +181 -0
- package/dist/recipes/toolRegistry.js +300 -0
- package/dist/recipes/toolRegistry.js.map +1 -0
- package/dist/recipes/tools/calendar.d.ts +6 -0
- package/dist/recipes/tools/calendar.js +61 -0
- package/dist/recipes/tools/calendar.js.map +1 -0
- package/dist/recipes/tools/confluence.d.ts +6 -0
- package/dist/recipes/tools/confluence.js +254 -0
- package/dist/recipes/tools/confluence.js.map +1 -0
- package/dist/recipes/tools/datadog.d.ts +6 -0
- package/dist/recipes/tools/datadog.js +239 -0
- package/dist/recipes/tools/datadog.js.map +1 -0
- package/dist/recipes/tools/diagnostics.d.ts +6 -0
- package/dist/recipes/tools/diagnostics.js +36 -0
- package/dist/recipes/tools/diagnostics.js.map +1 -0
- package/dist/recipes/tools/file.d.ts +6 -0
- package/dist/recipes/tools/file.js +170 -0
- package/dist/recipes/tools/file.js.map +1 -0
- package/dist/recipes/tools/git.d.ts +6 -0
- package/dist/recipes/tools/git.js +63 -0
- package/dist/recipes/tools/git.js.map +1 -0
- package/dist/recipes/tools/github.d.ts +6 -0
- package/dist/recipes/tools/github.js +91 -0
- package/dist/recipes/tools/github.js.map +1 -0
- package/dist/recipes/tools/gmail.d.ts +6 -0
- package/dist/recipes/tools/gmail.js +210 -0
- package/dist/recipes/tools/gmail.js.map +1 -0
- package/dist/recipes/tools/hubspot.d.ts +6 -0
- package/dist/recipes/tools/hubspot.js +232 -0
- package/dist/recipes/tools/hubspot.js.map +1 -0
- package/dist/recipes/tools/index.d.ts +22 -0
- package/dist/recipes/tools/index.js +25 -0
- package/dist/recipes/tools/index.js.map +1 -0
- package/dist/recipes/tools/intercom.d.ts +6 -0
- package/dist/recipes/tools/intercom.js +226 -0
- package/dist/recipes/tools/intercom.js.map +1 -0
- package/dist/recipes/tools/linear.d.ts +6 -0
- package/dist/recipes/tools/linear.js +83 -0
- package/dist/recipes/tools/linear.js.map +1 -0
- package/dist/recipes/tools/notion.d.ts +6 -0
- package/dist/recipes/tools/notion.js +278 -0
- package/dist/recipes/tools/notion.js.map +1 -0
- package/dist/recipes/tools/slack.d.ts +6 -0
- package/dist/recipes/tools/slack.js +72 -0
- package/dist/recipes/tools/slack.js.map +1 -0
- package/dist/recipes/tools/stripe.d.ts +6 -0
- package/dist/recipes/tools/stripe.js +265 -0
- package/dist/recipes/tools/stripe.js.map +1 -0
- package/dist/recipes/tools/zendesk.d.ts +6 -0
- package/dist/recipes/tools/zendesk.js +245 -0
- package/dist/recipes/tools/zendesk.js.map +1 -0
- package/dist/recipes/validation.d.ts +13 -0
- package/dist/recipes/validation.js +433 -0
- package/dist/recipes/validation.js.map +1 -0
- package/dist/recipes/yamlRunner.d.ts +87 -0
- package/dist/recipes/yamlRunner.js +693 -409
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/dist/recipesHttp.d.ts +34 -6
- package/dist/recipesHttp.js +285 -15
- package/dist/recipesHttp.js.map +1 -1
- package/dist/riskTier.js +1 -0
- package/dist/riskTier.js.map +1 -1
- package/dist/runLog.d.ts +23 -0
- package/dist/runLog.js +56 -1
- package/dist/runLog.js.map +1 -1
- package/dist/schemas/dry-run-plan.v1.json +139 -0
- package/dist/schemas/recipe.v1.json +684 -0
- package/dist/server.d.ts +32 -1
- package/dist/server.js +980 -97
- package/dist/server.js.map +1 -1
- package/dist/streamableHttp.js +2 -0
- package/dist/streamableHttp.js.map +1 -1
- package/dist/tools/addLinearComment.d.ts +55 -0
- package/dist/tools/addLinearComment.js +72 -0
- package/dist/tools/addLinearComment.js.map +1 -0
- package/dist/tools/bridgeDoctor.js +2 -2
- package/dist/tools/bridgeDoctor.js.map +1 -1
- package/dist/tools/createLinearIssue.d.ts +84 -0
- package/dist/tools/createLinearIssue.js +146 -0
- package/dist/tools/createLinearIssue.js.map +1 -0
- package/dist/tools/fetchCalendarEvents.d.ts +94 -0
- package/dist/tools/fetchCalendarEvents.js +97 -0
- package/dist/tools/fetchCalendarEvents.js.map +1 -0
- package/dist/tools/fetchGithubIssue.d.ts +80 -0
- package/dist/tools/fetchGithubIssue.js +84 -0
- package/dist/tools/fetchGithubIssue.js.map +1 -0
- package/dist/tools/fetchGithubPR.d.ts +89 -0
- package/dist/tools/fetchGithubPR.js +96 -0
- package/dist/tools/fetchGithubPR.js.map +1 -0
- package/dist/tools/fetchSlackProfile.d.ts +43 -0
- package/dist/tools/fetchSlackProfile.js +46 -0
- package/dist/tools/fetchSlackProfile.js.map +1 -0
- package/dist/tools/getConnectorStatus.d.ts +58 -0
- package/dist/tools/getConnectorStatus.js +56 -0
- package/dist/tools/getConnectorStatus.js.map +1 -0
- package/dist/tools/github/actions.js +4 -2
- package/dist/tools/github/actions.js.map +1 -1
- package/dist/tools/github/composite.d.ts +339 -0
- package/dist/tools/github/composite.js +343 -0
- package/dist/tools/github/composite.js.map +1 -0
- package/dist/tools/github/index.d.ts +2 -1
- package/dist/tools/github/index.js +2 -1
- package/dist/tools/github/index.js.map +1 -1
- package/dist/tools/github/issues.js +8 -4
- package/dist/tools/github/issues.js.map +1 -1
- package/dist/tools/github/pr.d.ts +122 -0
- package/dist/tools/github/pr.js +195 -5
- package/dist/tools/github/pr.js.map +1 -1
- package/dist/tools/index.js +32 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/searchTools.js +1 -1
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/tools/slackListChannels.d.ts +65 -0
- package/dist/tools/slackListChannels.js +70 -0
- package/dist/tools/slackListChannels.js.map +1 -0
- package/dist/tools/slackPostMessage.d.ts +57 -0
- package/dist/tools/slackPostMessage.js +77 -0
- package/dist/tools/slackPostMessage.js.map +1 -0
- package/dist/tools/testTraceToSource.js +2 -2
- package/dist/tools/testTraceToSource.js.map +1 -1
- package/dist/tools/updateLinearIssue.d.ts +89 -0
- package/dist/tools/updateLinearIssue.js +117 -0
- package/dist/tools/updateLinearIssue.js.map +1 -0
- package/dist/transport.d.ts +7 -1
- package/dist/transport.js +85 -11
- package/dist/transport.js.map +1 -1
- package/package.json +5 -2
- package/scripts/start-all.sh +56 -19
- package/templates/automation-policies/recipe-authoring.json +25 -0
- package/templates/automation-policy.example.json +6 -0
- package/templates/co.patchwork-os.bridge.plist +34 -0
- package/templates/recipes/ctx-loop-test.yaml +75 -0
- package/templates/recipes/lint-on-save.yaml +1 -2
- package/templates/recipes/morning-brief-slack.yaml +57 -0
- package/templates/recipes/morning-brief.yaml +14 -6
- package/templates/recipes/project-health-check.yaml +50 -0
- package/templates/recipes/sentry-to-linear.yaml +77 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NestedRecipeStep — handler for calling recipes from within recipes.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - Variable passing via template resolution
|
|
6
|
+
* - Isolated OutputRegistry for child
|
|
7
|
+
* - Risk escalation (child risk > parent risk uses child's)
|
|
8
|
+
* - Depth limiting (prevent infinite recursion)
|
|
9
|
+
*/
|
|
10
|
+
import { compileTemplate } from "./templateEngine.js";
|
|
11
|
+
/** Resolve template variables against parent context */
|
|
12
|
+
export function resolveNestedVars(vars, context) {
|
|
13
|
+
const resolved = {};
|
|
14
|
+
const errors = [];
|
|
15
|
+
for (const [key, templateStr] of Object.entries(vars)) {
|
|
16
|
+
const compiled = compileTemplate(templateStr);
|
|
17
|
+
const result = compiled.evaluate(context);
|
|
18
|
+
if ("error" in result) {
|
|
19
|
+
errors.push(result.error);
|
|
20
|
+
resolved[key] = "";
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
resolved[key] = result.value;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { resolved, errors };
|
|
27
|
+
}
|
|
28
|
+
/** Validate nested recipe call before execution */
|
|
29
|
+
export function validateNestedRecipe(config, context) {
|
|
30
|
+
if (context.currentDepth > context.recipeMaxDepth) {
|
|
31
|
+
return {
|
|
32
|
+
valid: false,
|
|
33
|
+
error: `Recipe nesting depth limit (${context.recipeMaxDepth}) exceeded. ` +
|
|
34
|
+
`Step "${config.id}" attempted to call "${config.recipe}" at depth ${context.currentDepth + 1}.`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (!config.recipe || typeof config.recipe !== "string") {
|
|
38
|
+
return {
|
|
39
|
+
valid: false,
|
|
40
|
+
error: `Invalid recipe reference in step "${config.id}": recipe name is required`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return { valid: true };
|
|
44
|
+
}
|
|
45
|
+
/** Calculate effective risk tier */
|
|
46
|
+
export function calculateNestedRisk(parentRisk, childRisk) {
|
|
47
|
+
const tiers = { low: 1, medium: 2, high: 3 };
|
|
48
|
+
const parentTier = tiers[parentRisk ?? "low"];
|
|
49
|
+
const childTier = tiers[childRisk ?? "low"];
|
|
50
|
+
const effective = Math.max(parentTier, childTier);
|
|
51
|
+
return effective === 1 ? "low" : effective === 2 ? "medium" : "high";
|
|
52
|
+
}
|
|
53
|
+
/** Format nested recipe result for parent registry */
|
|
54
|
+
export function formatNestedOutput(result, config) {
|
|
55
|
+
return {
|
|
56
|
+
stepId: config.output ?? config.id,
|
|
57
|
+
output: {
|
|
58
|
+
status: result.success ? "success" : "error",
|
|
59
|
+
data: result.success
|
|
60
|
+
? {
|
|
61
|
+
recipe: config.recipe,
|
|
62
|
+
result: result.data,
|
|
63
|
+
childOutputs: result.childOutputs,
|
|
64
|
+
}
|
|
65
|
+
: { error: result.error },
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** Mock nested recipe execution for dry-run mode */
|
|
70
|
+
export async function mockNestedRecipe(config, context) {
|
|
71
|
+
const validation = validateNestedRecipe(config, context);
|
|
72
|
+
if (!validation.valid) {
|
|
73
|
+
return { success: false, error: validation.error };
|
|
74
|
+
}
|
|
75
|
+
// Resolve templates to show what would be passed
|
|
76
|
+
const parentContext = context.parentRegistry.toTemplateContext(context.parentEnv);
|
|
77
|
+
const { resolved, errors } = resolveNestedVars(config.vars, parentContext);
|
|
78
|
+
if (errors.length > 0) {
|
|
79
|
+
return {
|
|
80
|
+
success: false,
|
|
81
|
+
error: `Template errors: ${errors.map((e) => e.message).join(", ")}`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
success: true,
|
|
86
|
+
data: {
|
|
87
|
+
dryRun: true,
|
|
88
|
+
recipe: config.recipe,
|
|
89
|
+
resolvedVars: resolved,
|
|
90
|
+
effectiveRisk: calculateNestedRisk(undefined, config.risk),
|
|
91
|
+
wouldExecuteAtDepth: context.currentDepth + 1,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=nestedRecipeStep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nestedRecipeStep.js","sourceRoot":"","sources":["../../src/recipes/nestedRecipeStep.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OutputRegistry — per-recipe-run state container for step outputs.
|
|
3
|
+
*
|
|
4
|
+
* Isolated from other runs, no persistence. Used by template engine
|
|
5
|
+
* to resolve {{steps.X.data.field}} references.
|
|
6
|
+
*/
|
|
7
|
+
import type { StepOutput, TemplateContext } from "./templateEngine.js";
|
|
8
|
+
export interface OutputRegistry {
|
|
9
|
+
/** Store output from a completed step */
|
|
10
|
+
set(stepId: string, output: StepOutput): void;
|
|
11
|
+
/** Get output for a step, or undefined if not yet run */
|
|
12
|
+
get(stepId: string): StepOutput | undefined;
|
|
13
|
+
/** Check if a step has completed */
|
|
14
|
+
has(stepId: string): boolean;
|
|
15
|
+
/** Get all step IDs that have outputs */
|
|
16
|
+
keys(): string[];
|
|
17
|
+
/** Convert to TemplateContext for template resolution */
|
|
18
|
+
toTemplateContext(env: Record<string, string | undefined>): TemplateContext;
|
|
19
|
+
/** Summary for logging/debugging */
|
|
20
|
+
summary(): {
|
|
21
|
+
total: number;
|
|
22
|
+
succeeded: number;
|
|
23
|
+
failed: number;
|
|
24
|
+
skipped: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Create a new isolated OutputRegistry for a recipe run */
|
|
28
|
+
export declare function createOutputRegistry(): OutputRegistry;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OutputRegistry — per-recipe-run state container for step outputs.
|
|
3
|
+
*
|
|
4
|
+
* Isolated from other runs, no persistence. Used by template engine
|
|
5
|
+
* to resolve {{steps.X.data.field}} references.
|
|
6
|
+
*/
|
|
7
|
+
class OutputRegistryImpl {
|
|
8
|
+
outputs = new Map();
|
|
9
|
+
set(stepId, output) {
|
|
10
|
+
this.outputs.set(stepId, output);
|
|
11
|
+
}
|
|
12
|
+
get(stepId) {
|
|
13
|
+
return this.outputs.get(stepId);
|
|
14
|
+
}
|
|
15
|
+
has(stepId) {
|
|
16
|
+
return this.outputs.has(stepId);
|
|
17
|
+
}
|
|
18
|
+
keys() {
|
|
19
|
+
return Array.from(this.outputs.keys());
|
|
20
|
+
}
|
|
21
|
+
toTemplateContext(env) {
|
|
22
|
+
const steps = {};
|
|
23
|
+
for (const [key, value] of this.outputs) {
|
|
24
|
+
steps[key] = value;
|
|
25
|
+
}
|
|
26
|
+
return { steps, env };
|
|
27
|
+
}
|
|
28
|
+
summary() {
|
|
29
|
+
let succeeded = 0;
|
|
30
|
+
let failed = 0;
|
|
31
|
+
let skipped = 0;
|
|
32
|
+
for (const output of this.outputs.values()) {
|
|
33
|
+
if (output.status === "success")
|
|
34
|
+
succeeded++;
|
|
35
|
+
else if (output.status === "error")
|
|
36
|
+
failed++;
|
|
37
|
+
else if (output.status === "skipped")
|
|
38
|
+
skipped++;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
total: this.outputs.size,
|
|
42
|
+
succeeded,
|
|
43
|
+
failed,
|
|
44
|
+
skipped,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Create a new isolated OutputRegistry for a recipe run */
|
|
49
|
+
export function createOutputRegistry() {
|
|
50
|
+
return new OutputRegistryImpl();
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=outputRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outputRegistry.js","sourceRoot":"","sources":["../../src/recipes/outputRegistry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA6BH,MAAM,kBAAkB;IACd,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAC;IAEhD,GAAG,CAAC,MAAc,EAAE,MAAkB;QACpC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,GAAG,CAAC,MAAc;QAChB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,GAAG,CAAC,MAAc;QAChB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,IAAI;QACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,iBAAiB,CAAC,GAAuC;QACvD,MAAM,KAAK,GAA+B,EAAE,CAAC;QAC7C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACxC,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACrB,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACxB,CAAC;IAED,OAAO;QAML,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3C,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;gBAAE,SAAS,EAAE,CAAC;iBACxC,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO;gBAAE,MAAM,EAAE,CAAC;iBACxC,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO,EAAE,CAAC;QAClD,CAAC;QACD,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;YACxB,SAAS;YACT,MAAM;YACN,OAAO;SACR,CAAC;IACJ,CAAC;CACF;AAED,4DAA4D;AAC5D,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,kBAAkB,EAAE,CAAC;AAClC,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
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
45
|
-
|
|
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,6 +1,9 @@
|
|
|
1
1
|
import { readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import
|
|
3
|
+
import cron from "node-cron";
|
|
4
|
+
import { parse as parseYaml } from "yaml";
|
|
5
|
+
import { loadConfig } from "../patchworkConfig.js";
|
|
6
|
+
import { findYamlRecipePath, loadRecipePrompt } from "../recipesHttp.js";
|
|
4
7
|
export class RecipeScheduler {
|
|
5
8
|
opts;
|
|
6
9
|
scheduled = [];
|
|
@@ -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
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
this.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
timer
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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,41 @@ export class RecipeScheduler {
|
|
|
59
118
|
}
|
|
60
119
|
stop() {
|
|
61
120
|
for (const entry of this.scheduled) {
|
|
62
|
-
|
|
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 = findYamlRecipePath(this.opts.recipesDir, name);
|
|
144
|
+
if (yamlPath) {
|
|
145
|
+
if (!this.opts.runYaml) {
|
|
146
|
+
this.opts.logger?.warn?.(`[scheduler] skipped "${name}" — YAML recipe requires runYaml callback (start bridge with --claude-driver)`);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
this.opts.runYaml(name).catch((err) => {
|
|
150
|
+
this.opts.logger?.warn?.(`[scheduler] YAML recipe "${name}" failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
151
|
+
});
|
|
152
|
+
this.opts.logger?.info?.(`[scheduler] fired YAML recipe "${name}"`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// JSON recipe — legacy path
|
|
74
156
|
const loaded = loadRecipePrompt(this.opts.recipesDir, name);
|
|
75
157
|
if (!loaded) {
|
|
76
158
|
this.opts.logger?.warn?.(`[scheduler] skipped "${name}" — recipe file disappeared`);
|
|
@@ -88,23 +170,31 @@ export class RecipeScheduler {
|
|
|
88
170
|
}
|
|
89
171
|
}
|
|
90
172
|
}
|
|
91
|
-
/** Parse @every forms into milliseconds. Returns null for unsupported schedules. */
|
|
173
|
+
/** Parse @every forms into milliseconds, or detect a 5-field cron expression. Returns null for unsupported schedules. */
|
|
92
174
|
export function parseSchedule(schedule) {
|
|
93
175
|
const trimmed = schedule.trim();
|
|
176
|
+
// @every Ns|Nm|Nh
|
|
94
177
|
const m = /^@every\s+(\d+)\s*(ms|s|m|h)$/i.exec(trimmed);
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
178
|
+
if (m) {
|
|
179
|
+
const n = Number.parseInt(m[1], 10);
|
|
180
|
+
const unit = m[2]?.toLowerCase();
|
|
181
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
182
|
+
return null;
|
|
183
|
+
const multiplier = unit === "ms"
|
|
184
|
+
? 1
|
|
185
|
+
: unit === "s"
|
|
186
|
+
? 1000
|
|
187
|
+
: unit === "m"
|
|
188
|
+
? 60_000
|
|
189
|
+
: 60 * 60_000;
|
|
190
|
+
return { kind: "interval", intervalMs: n * multiplier };
|
|
191
|
+
}
|
|
192
|
+
// 5-field cron expression (e.g. "0 8 * * 1-5")
|
|
193
|
+
if (/^\S+\s+\S+\s+\S+\s+\S+\s+\S+$/.test(trimmed)) {
|
|
194
|
+
if (cron.validate(trimmed)) {
|
|
195
|
+
return { kind: "cron5", expression: trimmed };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
109
199
|
}
|
|
110
200
|
//# sourceMappingURL=scheduler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/recipes/scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/recipes/scheduler.ts"],"names":[],"mappings":"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"}
|
package/dist/recipes/schema.d.ts
CHANGED
|
@@ -55,10 +55,25 @@ export interface ToolStep {
|
|
|
55
55
|
output?: string;
|
|
56
56
|
}
|
|
57
57
|
export type Step = AgentStep | ToolStep;
|
|
58
|
+
/**
|
|
59
|
+
* Recipe-level error policy. Single source of truth shared across
|
|
60
|
+
* chainedRunner, yamlRunner, and generated JSON schema.
|
|
61
|
+
*
|
|
62
|
+
* Currently-honored fields at runtime:
|
|
63
|
+
* - retry — integer; overridden per-step via step.retry
|
|
64
|
+
* - retryDelay — ms between retries (default 1000); overridden per-step
|
|
65
|
+
* - fallback — "log_only" and "deliver_original" both treat step
|
|
66
|
+
* failure as non-fatal (like optional: true) — fail-open.
|
|
67
|
+
* "abort" is the default (propagate).
|
|
68
|
+
* - notify — reserved; yamlRunner currently posts Slack notifications
|
|
69
|
+
* on any step failure when slack is connected. Gating on
|
|
70
|
+
* this flag is not yet wired.
|
|
71
|
+
*/
|
|
58
72
|
export interface ErrorPolicy {
|
|
59
|
-
notify?: boolean;
|
|
60
73
|
retry?: number;
|
|
61
|
-
|
|
74
|
+
retryDelay?: number;
|
|
75
|
+
fallback?: "log_only" | "abort" | "deliver_original";
|
|
76
|
+
notify?: boolean;
|
|
62
77
|
}
|
|
63
78
|
export interface Recipe {
|
|
64
79
|
name: string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema Generator — produces JSON Schema from tool registry.
|
|
3
|
+
*
|
|
4
|
+
* Generates composable schemas for editor autocomplete:
|
|
5
|
+
* - schemas/recipe.v1.json — top-level recipe schema
|
|
6
|
+
* - schemas/tools/<namespace>.json — per-namespace tool param schemas
|
|
7
|
+
*/
|
|
8
|
+
export interface SchemaSet {
|
|
9
|
+
/** Top-level recipe schema */
|
|
10
|
+
recipe: unknown;
|
|
11
|
+
/** Per-namespace tool schemas */
|
|
12
|
+
namespaces: Record<string, unknown>;
|
|
13
|
+
/** Dry-run plan JSON schema (machine-readable contract for `recipe run --dry-run` output) */
|
|
14
|
+
dryRunPlan: unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Generate complete schema set from current registry state.
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateSchemaSet(): SchemaSet;
|
|
20
|
+
/**
|
|
21
|
+
* JSON Schema for RecipeDryRunPlan. Consumers (dashboard run timeline, external CI)
|
|
22
|
+
* should pin on `schemaVersion` and validate against this.
|
|
23
|
+
*/
|
|
24
|
+
export declare function generateDryRunPlanSchema(): unknown;
|
|
25
|
+
/**
|
|
26
|
+
* Write schema files to disk.
|
|
27
|
+
*/
|
|
28
|
+
export declare function writeSchemas(outputDir: string, schemas: SchemaSet, writeFile: (path: string, content: string) => Promise<void> | void): Promise<void>;
|