patchwork-os 0.2.0-alpha.9 → 0.2.0-beta.0
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 +315 -35
- package/deploy/bootstrap-new-vps.sh +12 -12
- package/deploy/bootstrap-vps.sh +187 -0
- package/deploy/deploy-dashboard.sh +174 -0
- package/deploy/deploy-landing.sh +136 -0
- package/dist/activationMetrics.d.ts +67 -0
- package/dist/activationMetrics.js +255 -0
- package/dist/activationMetrics.js.map +1 -0
- package/dist/activityLog.d.ts +49 -0
- package/dist/activityLog.js +78 -0
- package/dist/activityLog.js.map +1 -1
- package/dist/approvalHttp.d.ts +49 -2
- package/dist/approvalHttp.js +217 -21
- package/dist/approvalHttp.js.map +1 -1
- package/dist/approvalInsights.d.ts +49 -0
- package/dist/approvalInsights.js +97 -0
- package/dist/approvalInsights.js.map +1 -0
- package/dist/approvalQueue.d.ts +27 -1
- package/dist/approvalQueue.js +123 -3
- package/dist/approvalQueue.js.map +1 -1
- package/dist/approvalSignals.d.ts +124 -0
- package/dist/approvalSignals.js +512 -0
- package/dist/approvalSignals.js.map +1 -0
- package/dist/automation.d.ts +57 -0
- package/dist/automation.js +156 -59
- package/dist/automation.js.map +1 -1
- package/dist/automationSuggestions.d.ts +79 -0
- package/dist/automationSuggestions.js +150 -0
- package/dist/automationSuggestions.js.map +1 -0
- package/dist/bridge.d.ts +3 -0
- package/dist/bridge.js +174 -143
- package/dist/bridge.js.map +1 -1
- package/dist/bridgeToken.js +57 -19
- package/dist/bridgeToken.js.map +1 -1
- package/dist/ccPermissions.d.ts +15 -0
- package/dist/ccPermissions.js +21 -4
- package/dist/ccPermissions.js.map +1 -1
- package/dist/claudeDriver.js +74 -16
- package/dist/claudeDriver.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/dashboard.js +1 -1
- package/dist/commands/dashboard.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/patchworkInit.d.ts +8 -0
- package/dist/commands/patchworkInit.js +77 -11
- package/dist/commands/patchworkInit.js.map +1 -1
- package/dist/commands/recipe.d.ts +289 -0
- package/dist/commands/recipe.js +1359 -0
- package/dist/commands/recipe.js.map +1 -0
- package/dist/commands/recipeInstall.d.ts +150 -0
- package/dist/commands/recipeInstall.js +647 -0
- package/dist/commands/recipeInstall.js.map +1 -0
- package/dist/commands/tracesExport.d.ts +83 -0
- package/dist/commands/tracesExport.js +269 -0
- package/dist/commands/tracesExport.js.map +1 -0
- package/dist/commands/tracesImport.d.ts +56 -0
- package/dist/commands/tracesImport.js +161 -0
- package/dist/commands/tracesImport.js.map +1 -0
- package/dist/config.d.ts +22 -1
- package/dist/config.js +108 -9
- package/dist/config.js.map +1 -1
- package/dist/connectorRoutes.d.ts +43 -0
- package/dist/connectorRoutes.js +1609 -0
- package/dist/connectorRoutes.js.map +1 -0
- package/dist/connectors/asana.d.ts +198 -0
- package/dist/connectors/asana.js +679 -0
- package/dist/connectors/asana.js.map +1 -0
- package/dist/connectors/baseConnector.d.ts +153 -0
- package/dist/connectors/baseConnector.js +336 -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/discord.d.ts +150 -0
- package/dist/connectors/discord.js +543 -0
- package/dist/connectors/discord.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.js +17 -18
- package/dist/connectors/github.js.map +1 -1
- package/dist/connectors/gitlab.d.ts +180 -0
- package/dist/connectors/gitlab.js +582 -0
- package/dist/connectors/gitlab.js.map +1 -0
- package/dist/connectors/gmail.d.ts +4 -1
- package/dist/connectors/gmail.js +149 -27
- package/dist/connectors/gmail.js.map +1 -1
- package/dist/connectors/googleCalendar.d.ts +4 -1
- package/dist/connectors/googleCalendar.js +88 -25
- package/dist/connectors/googleCalendar.js.map +1 -1
- package/dist/connectors/googleDrive.d.ts +34 -0
- package/dist/connectors/googleDrive.js +321 -0
- package/dist/connectors/googleDrive.js.map +1 -0
- package/dist/connectors/htmlEscape.d.ts +5 -0
- package/dist/connectors/htmlEscape.js +13 -0
- package/dist/connectors/htmlEscape.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.js +30 -19
- package/dist/connectors/linear.js.map +1 -1
- package/dist/connectors/mcpOAuth.d.ts +3 -0
- package/dist/connectors/mcpOAuth.js +64 -10
- package/dist/connectors/mcpOAuth.js.map +1 -1
- 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/oauthStateStore.d.ts +31 -0
- package/dist/connectors/oauthStateStore.js +52 -0
- package/dist/connectors/oauthStateStore.js.map +1 -0
- package/dist/connectors/pagerduty.d.ts +160 -0
- package/dist/connectors/pagerduty.js +464 -0
- package/dist/connectors/pagerduty.js.map +1 -0
- package/dist/connectors/sentry.js +5 -13
- package/dist/connectors/sentry.js.map +1 -1
- package/dist/connectors/slack.d.ts +16 -1
- package/dist/connectors/slack.js +155 -32
- package/dist/connectors/slack.js.map +1 -1
- 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 +484 -0
- package/dist/connectors/tokenStorage.js.map +1 -0
- package/dist/connectors/zendesk.d.ts +104 -0
- package/dist/connectors/zendesk.js +442 -0
- package/dist/connectors/zendesk.js.map +1 -0
- package/dist/cors.d.ts +10 -0
- package/dist/cors.js +29 -0
- package/dist/cors.js.map +1 -0
- package/dist/decisionReplay.d.ts +72 -0
- package/dist/decisionReplay.js +92 -0
- package/dist/decisionReplay.js.map +1 -0
- package/dist/decisionTraceLog.d.ts +6 -0
- package/dist/decisionTraceLog.js +54 -2
- package/dist/decisionTraceLog.js.map +1 -1
- 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 +79 -0
- package/dist/featureFlags.js +208 -0
- package/dist/featureFlags.js.map +1 -0
- package/dist/fp/automationInterpreter.js +26 -21
- 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/automationState.js +4 -1
- package/dist/fp/automationState.js.map +1 -1
- package/dist/fp/policyParser.js +21 -1
- package/dist/fp/policyParser.js.map +1 -1
- package/dist/inboxRoutes.d.ts +22 -0
- package/dist/inboxRoutes.js +114 -0
- package/dist/inboxRoutes.js.map +1 -0
- package/dist/index.js +1400 -201
- 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/mcpRoutes.d.ts +37 -0
- package/dist/mcpRoutes.js +76 -0
- package/dist/mcpRoutes.js.map +1 -0
- package/dist/oauth.d.ts +7 -1
- package/dist/oauth.js +201 -39
- package/dist/oauth.js.map +1 -1
- package/dist/oauthRoutes.d.ts +32 -0
- package/dist/oauthRoutes.js +124 -0
- package/dist/oauthRoutes.js.map +1 -0
- package/dist/orchestrator/orchestratorBridge.js +2 -2
- package/dist/orchestrator/orchestratorBridge.js.map +1 -1
- package/dist/patchworkConfig.d.ts +16 -0
- package/dist/patchworkConfig.js +1 -1
- package/dist/patchworkConfig.js.map +1 -1
- package/dist/pluginLoader.d.ts +28 -0
- package/dist/pluginLoader.js +77 -11
- package/dist/pluginLoader.js.map +1 -1
- package/dist/pluginWatcher.js +8 -3
- package/dist/pluginWatcher.js.map +1 -1
- package/dist/preToolUseHook.d.ts +12 -0
- package/dist/preToolUseHook.js +23 -0
- package/dist/preToolUseHook.js.map +1 -1
- package/dist/recipeOrchestration.d.ts +121 -0
- package/dist/recipeOrchestration.js +955 -0
- package/dist/recipeOrchestration.js.map +1 -0
- package/dist/recipeRoutes.d.ts +180 -0
- package/dist/recipeRoutes.js +1345 -0
- package/dist/recipeRoutes.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 +29 -0
- package/dist/recipes/agentExecutor.js +49 -0
- package/dist/recipes/agentExecutor.js.map +1 -0
- package/dist/recipes/chainedRunner.d.ts +191 -0
- package/dist/recipes/chainedRunner.js +759 -0
- package/dist/recipes/chainedRunner.js.map +1 -0
- package/dist/recipes/compiler.js +3 -3
- package/dist/recipes/compiler.js.map +1 -1
- 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/disabledMarkers.d.ts +48 -0
- package/dist/recipes/disabledMarkers.js +52 -0
- package/dist/recipes/disabledMarkers.js.map +1 -0
- package/dist/recipes/installer.js +3 -3
- package/dist/recipes/installer.js.map +1 -1
- package/dist/recipes/legacyRecipeCompat.d.ts +10 -0
- package/dist/recipes/legacyRecipeCompat.js +131 -0
- package/dist/recipes/legacyRecipeCompat.js.map +1 -0
- package/dist/recipes/manifest.d.ts +47 -0
- package/dist/recipes/manifest.js +156 -0
- package/dist/recipes/manifest.js.map +1 -0
- package/dist/recipes/migrationWarnings.d.ts +12 -0
- package/dist/recipes/migrationWarnings.js +44 -0
- package/dist/recipes/migrationWarnings.js.map +1 -0
- package/dist/recipes/migrations/index.d.ts +24 -0
- package/dist/recipes/migrations/index.js +55 -0
- package/dist/recipes/migrations/index.js.map +1 -0
- package/dist/recipes/migrations/types.d.ts +28 -0
- package/dist/recipes/migrations/types.js +2 -0
- package/dist/recipes/migrations/types.js.map +1 -0
- package/dist/recipes/migrations/v1.d.ts +11 -0
- package/dist/recipes/migrations/v1.js +18 -0
- package/dist/recipes/migrations/v1.js.map +1 -0
- package/dist/recipes/names.d.ts +40 -0
- package/dist/recipes/names.js +66 -0
- package/dist/recipes/names.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/parser.js +4 -1
- package/dist/recipes/parser.js.map +1 -1
- package/dist/recipes/replayRun.d.ts +62 -0
- package/dist/recipes/replayRun.js +97 -0
- package/dist/recipes/replayRun.js.map +1 -0
- package/dist/recipes/resolveRecipePath.d.ts +69 -0
- package/dist/recipes/resolveRecipePath.js +202 -0
- package/dist/recipes/resolveRecipePath.js.map +1 -0
- package/dist/recipes/scheduler.d.ts +23 -7
- package/dist/recipes/scheduler.js +225 -45
- 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/stepObservation.d.ts +44 -0
- package/dist/recipes/stepObservation.js +232 -0
- package/dist/recipes/stepObservation.js.map +1 -0
- package/dist/recipes/templateEngine.d.ts +62 -0
- package/dist/recipes/templateEngine.js +201 -0
- package/dist/recipes/templateEngine.js.map +1 -0
- package/dist/recipes/toolRegistry.d.ts +186 -0
- package/dist/recipes/toolRegistry.js +309 -0
- package/dist/recipes/toolRegistry.js.map +1 -0
- package/dist/recipes/tools/asana.d.ts +16 -0
- package/dist/recipes/tools/asana.js +524 -0
- package/dist/recipes/tools/asana.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/discord.d.ts +18 -0
- package/dist/recipes/tools/discord.js +254 -0
- package/dist/recipes/tools/discord.js.map +1 -0
- package/dist/recipes/tools/file.d.ts +12 -0
- package/dist/recipes/tools/file.js +174 -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 +116 -0
- package/dist/recipes/tools/github.js.map +1 -0
- package/dist/recipes/tools/gitlab.d.ts +11 -0
- package/dist/recipes/tools/gitlab.js +285 -0
- package/dist/recipes/tools/gitlab.js.map +1 -0
- package/dist/recipes/tools/gmail.d.ts +6 -0
- package/dist/recipes/tools/gmail.js +434 -0
- package/dist/recipes/tools/gmail.js.map +1 -0
- package/dist/recipes/tools/googleDrive.d.ts +1 -0
- package/dist/recipes/tools/googleDrive.js +55 -0
- package/dist/recipes/tools/googleDrive.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 +30 -0
- package/dist/recipes/tools/index.js +33 -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/jira.d.ts +14 -0
- package/dist/recipes/tools/jira.js +369 -0
- package/dist/recipes/tools/jira.js.map +1 -0
- package/dist/recipes/tools/linear.d.ts +7 -0
- package/dist/recipes/tools/linear.js +307 -0
- package/dist/recipes/tools/linear.js.map +1 -0
- package/dist/recipes/tools/meetingNotes.d.ts +21 -0
- package/dist/recipes/tools/meetingNotes.js +701 -0
- package/dist/recipes/tools/meetingNotes.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/pagerduty.d.ts +15 -0
- package/dist/recipes/tools/pagerduty.js +451 -0
- package/dist/recipes/tools/pagerduty.js.map +1 -0
- package/dist/recipes/tools/sentry.d.ts +12 -0
- package/dist/recipes/tools/sentry.js +73 -0
- package/dist/recipes/tools/sentry.js.map +1 -0
- package/dist/recipes/tools/slack.d.ts +6 -0
- package/dist/recipes/tools/slack.js +82 -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 +617 -0
- package/dist/recipes/validation.js.map +1 -0
- package/dist/recipes/yamlRunner.d.ts +116 -1
- package/dist/recipes/yamlRunner.js +1000 -401
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/dist/recipesHttp.d.ts +137 -6
- package/dist/recipesHttp.js +941 -29
- package/dist/recipesHttp.js.map +1 -1
- package/dist/riskTier.js +7 -1
- package/dist/riskTier.js.map +1 -1
- package/dist/runLog.d.ts +100 -1
- package/dist/runLog.js +258 -5
- 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 +121 -8
- package/dist/server.js +538 -735
- package/dist/server.js.map +1 -1
- package/dist/ssrfGuard.d.ts +54 -0
- package/dist/ssrfGuard.js +122 -0
- package/dist/ssrfGuard.js.map +1 -0
- package/dist/streamableHttp.d.ts +39 -1
- package/dist/streamableHttp.js +128 -17
- package/dist/streamableHttp.js.map +1 -1
- package/dist/tokenUsageTracker.d.ts +33 -0
- package/dist/tokenUsageTracker.js +146 -0
- package/dist/tokenUsageTracker.js.map +1 -0
- package/dist/tools/activityLog.d.ts +2 -0
- package/dist/tools/addLinearComment.d.ts +1 -0
- package/dist/tools/addLinearComment.js +4 -2
- package/dist/tools/addLinearComment.js.map +1 -1
- package/dist/tools/batchLsp.d.ts +3 -0
- package/dist/tools/bridgeDoctor.d.ts +1 -0
- package/dist/tools/bridgeDoctor.js +2 -2
- package/dist/tools/bridgeDoctor.js.map +1 -1
- package/dist/tools/bridgeStatus.d.ts +1 -0
- package/dist/tools/cancelClaudeTask.d.ts +2 -0
- package/dist/tools/cancelClaudeTask.js +1 -0
- package/dist/tools/cancelClaudeTask.js.map +1 -1
- package/dist/tools/checkDocumentDirty.d.ts +1 -0
- package/dist/tools/clipboard.d.ts +2 -0
- package/dist/tools/closeTabs.d.ts +2 -0
- package/dist/tools/codeLens.d.ts +1 -0
- package/dist/tools/contextBundle.d.ts +1 -0
- package/dist/tools/createIssueFromAIComment.d.ts +1 -0
- package/dist/tools/createLinearIssue.d.ts +1 -0
- package/dist/tools/ctxGetTaskContext.d.ts +1 -0
- package/dist/tools/ctxQueryTraces.d.ts +1 -0
- package/dist/tools/ctxSaveTrace.d.ts +1 -0
- package/dist/tools/debug.d.ts +4 -0
- package/dist/tools/decorations.d.ts +2 -0
- package/dist/tools/documentLinks.d.ts +1 -0
- package/dist/tools/editText.d.ts +1 -0
- package/dist/tools/enrichCommit.d.ts +1 -0
- package/dist/tools/enrichStackTrace.d.ts +1 -0
- package/dist/tools/explainDiagnostic.d.ts +1 -0
- package/dist/tools/explainSymbol.d.ts +1 -0
- package/dist/tools/fetchCalendarEvents.d.ts +1 -0
- package/dist/tools/fetchGithubIssue.d.ts +1 -0
- package/dist/tools/fetchGithubPR.d.ts +1 -0
- package/dist/tools/fetchLinearIssue.d.ts +1 -0
- package/dist/tools/fetchSentryIssue.d.ts +1 -0
- package/dist/tools/fetchSlackProfile.d.ts +1 -0
- package/dist/tools/fetchSlackProfile.js +4 -1
- package/dist/tools/fetchSlackProfile.js.map +1 -1
- package/dist/tools/fileOperations.d.ts +3 -0
- package/dist/tools/fileWatcher.d.ts +2 -0
- package/dist/tools/findFiles.d.ts +1 -0
- package/dist/tools/findRelatedTests.d.ts +1 -0
- package/dist/tools/fixAllLintErrors.d.ts +1 -0
- package/dist/tools/foldingRanges.d.ts +1 -0
- package/dist/tools/formatDocument.d.ts +1 -0
- package/dist/tools/generateTests.d.ts +1 -0
- package/dist/tools/getAIComments.d.ts +1 -0
- package/dist/tools/getAnalyticsReport.d.ts +1 -0
- package/dist/tools/getArchitectureContext.d.ts +1 -0
- package/dist/tools/getBufferContent.d.ts +1 -0
- package/dist/tools/getChangeImpact.d.ts +1 -0
- package/dist/tools/getClaudeTaskStatus.d.ts +2 -0
- package/dist/tools/getClaudeTaskStatus.js +1 -0
- package/dist/tools/getClaudeTaskStatus.js.map +1 -1
- package/dist/tools/getCodeCoverage.d.ts +1 -0
- package/dist/tools/getCommitsForIssue.d.ts +1 -0
- package/dist/tools/getConnectorStatus.d.ts +1 -0
- package/dist/tools/getCurrentSelection.d.ts +2 -0
- package/dist/tools/getDebugState.d.ts +1 -0
- package/dist/tools/getDependencyTree.d.ts +1 -0
- package/dist/tools/getDiagnostics.d.ts +1 -0
- package/dist/tools/getDiffFromHandoff.d.ts +1 -0
- package/dist/tools/getDocumentSymbols.d.ts +25 -0
- package/dist/tools/getDocumentSymbols.js +74 -8
- package/dist/tools/getDocumentSymbols.js.map +1 -1
- package/dist/tools/getFileTree.d.ts +1 -0
- package/dist/tools/getGitDiff.d.ts +1 -0
- package/dist/tools/getGitHotspots.d.ts +1 -0
- package/dist/tools/getGitLog.d.ts +1 -0
- package/dist/tools/getGitStatus.d.ts +1 -0
- package/dist/tools/getImportTree.d.ts +1 -0
- package/dist/tools/getImportedSignatures.d.ts +1 -0
- package/dist/tools/getOpenEditors.d.ts +1 -0
- package/dist/tools/getPRTemplate.d.ts +1 -0
- package/dist/tools/getProjectContext.d.ts +1 -0
- package/dist/tools/getProjectInfo.d.ts +1 -0
- package/dist/tools/getSecurityAdvisories.d.ts +1 -0
- package/dist/tools/getSecurityAdvisories.js +10 -1
- package/dist/tools/getSecurityAdvisories.js.map +1 -1
- package/dist/tools/getSessionUsage.d.ts +4 -0
- package/dist/tools/getSessionUsage.js +3 -0
- package/dist/tools/getSessionUsage.js.map +1 -1
- package/dist/tools/getSymbolHistory.d.ts +1 -0
- package/dist/tools/getToolCapabilities.d.ts +1 -0
- package/dist/tools/getTypeSignature.d.ts +1 -0
- package/dist/tools/getWorkspaceFolders.d.ts +1 -0
- package/dist/tools/getWorkspaceSettings.d.ts +1 -0
- package/dist/tools/gitHistory.d.ts +2 -0
- package/dist/tools/gitWrite.d.ts +11 -0
- package/dist/tools/github/actions.d.ts +2 -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 +342 -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 +1 -0
- package/dist/tools/github/index.js +1 -0
- package/dist/tools/github/index.js.map +1 -1
- package/dist/tools/github/issues.d.ts +4 -0
- package/dist/tools/github/issues.js +8 -4
- package/dist/tools/github/issues.js.map +1 -1
- package/dist/tools/github/pr.d.ts +7 -0
- package/dist/tools/github/pr.js +50 -12
- package/dist/tools/github/pr.js.map +1 -1
- package/dist/tools/handoffNote.d.ts +4 -0
- package/dist/tools/handoffNote.js +2 -0
- package/dist/tools/handoffNote.js.map +1 -1
- package/dist/tools/hoverAtCursor.d.ts +1 -0
- package/dist/tools/httpClient.d.ts +2 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.js +47 -8
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/inlayHints.d.ts +1 -0
- package/dist/tools/launchQuickTask.d.ts +2 -0
- package/dist/tools/launchQuickTask.js +1 -0
- package/dist/tools/launchQuickTask.js.map +1 -1
- package/dist/tools/listClaudeTasks.d.ts +2 -0
- package/dist/tools/listClaudeTasks.js +1 -0
- package/dist/tools/listClaudeTasks.js.map +1 -1
- package/dist/tools/listTerminals.d.ts +1 -0
- package/dist/tools/lsp.d.ts +14 -0
- package/dist/tools/navigateToSymbolByName.d.ts +1 -0
- package/dist/tools/openDiff.d.ts +1 -0
- package/dist/tools/openFile.d.ts +1 -0
- package/dist/tools/openInBrowser.d.ts +1 -0
- package/dist/tools/organizeImports.d.ts +1 -0
- package/dist/tools/performanceReport.d.ts +1 -0
- package/dist/tools/planPersistence.d.ts +5 -0
- package/dist/tools/previewEdit.d.ts +1 -0
- package/dist/tools/refactorAnalyze.d.ts +1 -0
- package/dist/tools/refactorPreview.d.ts +2 -0
- package/dist/tools/refactorPreview.js +1 -0
- package/dist/tools/refactorPreview.js.map +1 -1
- package/dist/tools/replaceBlock.d.ts +1 -0
- package/dist/tools/resumeClaudeTask.d.ts +2 -0
- package/dist/tools/resumeClaudeTask.js +1 -0
- package/dist/tools/resumeClaudeTask.js.map +1 -1
- package/dist/tools/runClaudeTask.d.ts +2 -0
- package/dist/tools/runClaudeTask.js +1 -0
- package/dist/tools/runClaudeTask.js.map +1 -1
- package/dist/tools/runCommand.d.ts +1 -0
- package/dist/tools/runTests.d.ts +1 -0
- package/dist/tools/saveDocument.d.ts +1 -0
- package/dist/tools/screenshotAndAnnotate.d.ts +1 -0
- package/dist/tools/searchAndReplace.d.ts +1 -0
- package/dist/tools/searchTools.d.ts +1 -0
- package/dist/tools/searchTools.js +1 -1
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/tools/searchWorkspace.d.ts +1 -0
- package/dist/tools/selectionRanges.d.ts +1 -0
- package/dist/tools/semanticTokens.d.ts +1 -0
- package/dist/tools/setActiveWorkspaceFolder.d.ts +1 -0
- package/dist/tools/signatureHelp.d.ts +1 -0
- package/dist/tools/slackListChannels.d.ts +1 -0
- package/dist/tools/slackListChannels.js.map +1 -1
- package/dist/tools/slackPostMessage.d.ts +1 -0
- package/dist/tools/slackPostMessage.js +11 -6
- package/dist/tools/slackPostMessage.js.map +1 -1
- package/dist/tools/terminal.d.ts +6 -0
- package/dist/tools/testTraceToSource.d.ts +1 -0
- package/dist/tools/testTraceToSource.js +2 -2
- package/dist/tools/testTraceToSource.js.map +1 -1
- package/dist/tools/transaction.d.ts +23 -0
- package/dist/tools/transaction.js +29 -0
- package/dist/tools/transaction.js.map +1 -1
- package/dist/tools/typeHierarchy.d.ts +1 -0
- package/dist/tools/updateLinearIssue.d.ts +1 -0
- package/dist/tools/updateLinearIssue.js +20 -6
- package/dist/tools/updateLinearIssue.js.map +1 -1
- package/dist/tools/utils.d.ts +2 -0
- package/dist/tools/utils.js.map +1 -1
- package/dist/tools/vscodeCommands.d.ts +2 -0
- package/dist/tools/vscodeTasks.d.ts +2 -0
- package/dist/tools/workspaceSettings.d.ts +1 -0
- package/dist/traceEncryption.d.ts +46 -0
- package/dist/traceEncryption.js +124 -0
- package/dist/traceEncryption.js.map +1 -0
- package/dist/transport.d.ts +46 -1
- package/dist/transport.js +173 -19
- package/dist/transport.js.map +1 -1
- package/package.json +30 -8
- package/scripts/mcp-stdio-shim.cjs +19 -3
- package/scripts/start-all.sh +30 -1
- 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/policies/README.md +72 -0
- package/templates/policies/conservative.json +14 -0
- package/templates/policies/developer.json +14 -0
- package/templates/policies/headless-ci.json +24 -0
- package/templates/policies/personal-assistant.json +15 -0
- package/templates/policies/regulated-industry.json +18 -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 +2 -2
- package/templates/recipes/project-health-check.yaml +50 -0
- package/templates/recipes/webhook/README.md +70 -0
- package/templates/recipes/webhook/capture-thought.yaml +26 -0
- package/templates/recipes/webhook/customer-escalation.yaml +49 -0
- package/templates/recipes/webhook/incident-intake.yaml +46 -0
- package/templates/recipes/webhook/meeting-prep.yaml +48 -0
- package/templates/recipes/webhook/morning-brief.yaml +57 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resolveRecipePath — recipe-runner path jail.
|
|
3
|
+
*
|
|
4
|
+
* Closes G-security F-01 (CRITICAL — `file.read/write/append` accept any
|
|
5
|
+
* absolute path), F-02 (CRITICAL — template-substituted vars escape via
|
|
6
|
+
* `..`), and the R2 C-1 chained-runner third-substitution-site gap.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors the symlink-walking strategy from `src/tools/utils.ts:104-200`
|
|
9
|
+
* (`resolveFilePath`) but operates against an allowlist of recipe-roots
|
|
10
|
+
* rather than a single workspace root:
|
|
11
|
+
*
|
|
12
|
+
* - `~/.patchwork/` (always allowed — recipe install dir)
|
|
13
|
+
* - the bridge / CLI workspace (always allowed — passed in via `opts.workspace`)
|
|
14
|
+
* - `os.tmpdir()` (OFF by default; opt-in via the
|
|
15
|
+
* `CLAUDE_IDE_BRIDGE_RECIPE_TMP_JAIL=1`
|
|
16
|
+
* env var, per R2 C-2 maintainer decision)
|
|
17
|
+
*
|
|
18
|
+
* On any escape (null byte, segment outside all roots, symlink target
|
|
19
|
+
* outside roots, hardlink on a write target) the helper throws an `Error`
|
|
20
|
+
* with `err.code = "recipe_path_jail_escape"`. Callers and tests must
|
|
21
|
+
* assert on `err.code`, never on message text (R2 M-4).
|
|
22
|
+
*
|
|
23
|
+
* Defense-in-depth — apply at every layer:
|
|
24
|
+
* - `src/recipes/tools/file.ts` (per-tool execute())
|
|
25
|
+
* - `src/recipes/yamlRunner.ts:976-994` (default StepDeps file ops)
|
|
26
|
+
* - `src/recipes/yamlRunner.ts:642` (post-render path snapshot)
|
|
27
|
+
* - `src/recipes/yamlRunner.ts:1252-1262` (chained-runner executeTool)
|
|
28
|
+
* - `src/recipes/chainedRunner.ts:194-205` (template-substitution site)
|
|
29
|
+
* - `src/recipeRoutes.ts:131-138 :172-181` (HTTP vars validator)
|
|
30
|
+
* - `src/commands/recipe.ts:1080-1102` (CLI warn on out-of-jail recipe ref)
|
|
31
|
+
*/
|
|
32
|
+
import fs from "node:fs";
|
|
33
|
+
import os from "node:os";
|
|
34
|
+
import path from "node:path";
|
|
35
|
+
/** Build a jail error with the canonical code. Never expose internals via message-matching. */
|
|
36
|
+
function jailError(message) {
|
|
37
|
+
const err = new Error(message);
|
|
38
|
+
err.code = "recipe_path_jail_escape";
|
|
39
|
+
return err;
|
|
40
|
+
}
|
|
41
|
+
/** Expand a leading `~/` segment using `os.homedir()` (or the test override). */
|
|
42
|
+
function expandHome(p, homeDir) {
|
|
43
|
+
if (p === "~")
|
|
44
|
+
return homeDir;
|
|
45
|
+
if (p.startsWith("~/"))
|
|
46
|
+
return path.join(homeDir, p.slice(2));
|
|
47
|
+
return p;
|
|
48
|
+
}
|
|
49
|
+
/** Compute the active jail roots given the runtime opts. */
|
|
50
|
+
function jailRoots(opts) {
|
|
51
|
+
const homeDir = opts.homeDir ?? os.homedir();
|
|
52
|
+
const allowTmp = opts.allowTmp ?? process.env.CLAUDE_IDE_BRIDGE_RECIPE_TMP_JAIL === "1";
|
|
53
|
+
const workspace = opts.workspace ?? process.cwd();
|
|
54
|
+
const roots = [path.resolve(homeDir, ".patchwork"), path.resolve(workspace)];
|
|
55
|
+
if (allowTmp) {
|
|
56
|
+
// On macOS `os.tmpdir()` returns `/var/folders/...` but the conventional
|
|
57
|
+
// `/tmp` symlink points at `/private/tmp` — we expose both so a recipe
|
|
58
|
+
// (or a legacy test) that hard-codes `/tmp/...` resolves cleanly. The
|
|
59
|
+
// symlink-aware realpath check below will still reject anything whose
|
|
60
|
+
// physical target is outside both roots.
|
|
61
|
+
roots.push(path.resolve(os.tmpdir()));
|
|
62
|
+
roots.push("/tmp");
|
|
63
|
+
}
|
|
64
|
+
// Dedupe — workspace==tmpdir on some CI runners would double-count and
|
|
65
|
+
// confuse the "outside all roots" reject branch.
|
|
66
|
+
return Array.from(new Set(roots));
|
|
67
|
+
}
|
|
68
|
+
/** True if `target` is inside (or equal to) any allowed jail root. */
|
|
69
|
+
function isInsideAnyRoot(target, roots) {
|
|
70
|
+
for (const root of roots) {
|
|
71
|
+
if (target === root)
|
|
72
|
+
return true;
|
|
73
|
+
if (target.startsWith(root + path.sep))
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Walk up the ancestor chain of a (possibly non-existent) path, returning
|
|
80
|
+
* the realpath of the first ancestor that exists on disk plus the unresolved
|
|
81
|
+
* suffix. Mirrors `src/tools/utils.ts:130-177` so a symlink anywhere along
|
|
82
|
+
* the chain (including the bridge / install dir) is followed before the
|
|
83
|
+
* containment check.
|
|
84
|
+
*/
|
|
85
|
+
function realpathOrAncestor(p) {
|
|
86
|
+
try {
|
|
87
|
+
return fs.realpathSync(p);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
let ancestor = path.dirname(p);
|
|
91
|
+
const suffix = [path.basename(p)];
|
|
92
|
+
while (ancestor !== path.dirname(ancestor)) {
|
|
93
|
+
try {
|
|
94
|
+
const realAncestor = fs.realpathSync(ancestor);
|
|
95
|
+
return path.join(realAncestor, ...suffix);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
suffix.unshift(path.basename(ancestor));
|
|
99
|
+
ancestor = path.dirname(ancestor);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Reached fs root without finding a real ancestor — fail closed; the
|
|
103
|
+
// caller will translate this to a jail-escape rather than skip the
|
|
104
|
+
// containment check.
|
|
105
|
+
throw new Error(`no real ancestor found for "${p}"`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Resolve a recipe-supplied path, expanding `~/`, normalising, and asserting
|
|
110
|
+
* the result lives inside one of the jail roots after symlink resolution.
|
|
111
|
+
*
|
|
112
|
+
* Throws `RecipePathJailError` (code `"recipe_path_jail_escape"`) on any
|
|
113
|
+
* containment violation. Callers should propagate the error unchanged so
|
|
114
|
+
* tests can assert on `err.code`.
|
|
115
|
+
*/
|
|
116
|
+
export function resolveRecipePath(rawPath, opts = {}) {
|
|
117
|
+
if (typeof rawPath !== "string") {
|
|
118
|
+
throw jailError("recipe path must be a string");
|
|
119
|
+
}
|
|
120
|
+
if (rawPath.length === 0) {
|
|
121
|
+
throw jailError("recipe path must not be empty");
|
|
122
|
+
}
|
|
123
|
+
if (rawPath.includes("\x00")) {
|
|
124
|
+
throw jailError("recipe path must not contain null bytes");
|
|
125
|
+
}
|
|
126
|
+
const homeDir = opts.homeDir ?? os.homedir();
|
|
127
|
+
const expanded = expandHome(rawPath, homeDir);
|
|
128
|
+
const resolved = path.isAbsolute(expanded)
|
|
129
|
+
? path.resolve(expanded)
|
|
130
|
+
: path.resolve(opts.workspace ?? process.cwd(), expanded);
|
|
131
|
+
const roots = jailRoots(opts);
|
|
132
|
+
// Lexical containment first — cheap reject for `..` segments resolving
|
|
133
|
+
// outside any root before we do any FS calls.
|
|
134
|
+
if (!isInsideAnyRoot(resolved, roots)) {
|
|
135
|
+
throw jailError(`recipe path "${rawPath}" resolves outside the allowed jail roots`);
|
|
136
|
+
}
|
|
137
|
+
// Symlink-aware re-check. We resolve the realpath of every existing
|
|
138
|
+
// ancestor so a link at any level (including a freshly-installed recipe
|
|
139
|
+
// dir pointing at `/tmp`) cannot bypass the lexical check above.
|
|
140
|
+
let real;
|
|
141
|
+
try {
|
|
142
|
+
real = realpathOrAncestor(resolved);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
throw jailError(`recipe path "${rawPath}" failed symlink resolution: ${err instanceof Error ? err.message : String(err)}`);
|
|
146
|
+
}
|
|
147
|
+
const realRoots = [];
|
|
148
|
+
for (const root of roots) {
|
|
149
|
+
try {
|
|
150
|
+
realRoots.push(fs.realpathSync(root));
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// Root does not exist yet (e.g. ~/.patchwork on a fresh install).
|
|
154
|
+
// Use the resolved (lexical) form — `mkdirSync({recursive:true})`
|
|
155
|
+
// will create it inside the lexical jail anyway, and the symlink
|
|
156
|
+
// walk above already confirmed nothing on disk redirects out.
|
|
157
|
+
realRoots.push(root);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (!isInsideAnyRoot(real, realRoots)) {
|
|
161
|
+
throw jailError(`recipe path "${rawPath}" escapes jail via symlink (real target "${real}")`);
|
|
162
|
+
}
|
|
163
|
+
// Hardlink guard for write paths — same rationale as `resolveFilePath`'s
|
|
164
|
+
// `opts.write` branch: a hardlink from inside the jail to an outside
|
|
165
|
+
// file shares an inode and passes the realpath check, but writing
|
|
166
|
+
// through it would modify the outside file.
|
|
167
|
+
if (opts.write) {
|
|
168
|
+
try {
|
|
169
|
+
const lst = fs.lstatSync(resolved);
|
|
170
|
+
if (!lst.isDirectory() && lst.nlink > 1) {
|
|
171
|
+
throw jailError(`recipe path "${rawPath}" is a hardlink (nlink=${lst.nlink}); writes denied to prevent jail escape`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
// ENOENT — file doesn't exist yet, safe to create. Re-throw if it's
|
|
176
|
+
// already a jail error (the nlink branch above).
|
|
177
|
+
if (err instanceof Error &&
|
|
178
|
+
err.code === "recipe_path_jail_escape") {
|
|
179
|
+
throw err;
|
|
180
|
+
}
|
|
181
|
+
// Other lstat errors (EACCES, etc.) — non-fatal; the write call will
|
|
182
|
+
// surface them with the OS-level message.
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return resolved;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Side-effect-free predicate variant — returns `null` on jail escape rather
|
|
189
|
+
* than throwing. Used by the CLI `recipe run` warn path (F-10), which wants
|
|
190
|
+
* to write a stderr notice when a recipe **file** lives outside the jail
|
|
191
|
+
* but still loads it (the YAML loader is a separate trust boundary from the
|
|
192
|
+
* tool dispatch jail).
|
|
193
|
+
*/
|
|
194
|
+
export function tryResolveRecipePath(rawPath, opts = {}) {
|
|
195
|
+
try {
|
|
196
|
+
return resolveRecipePath(rawPath, opts);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=resolveRecipePath.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveRecipePath.js","sourceRoot":"","sources":["../../src/recipes/resolveRecipePath.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,10 @@
|
|
|
1
|
-
import { readdirSync, readFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } 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";
|
|
7
|
+
import { getConfigDisabledNames, isInstallDirDisabled, } from "./disabledMarkers.js";
|
|
4
8
|
export class RecipeScheduler {
|
|
5
9
|
opts;
|
|
6
10
|
scheduled = [];
|
|
@@ -13,6 +17,14 @@ export class RecipeScheduler {
|
|
|
13
17
|
}
|
|
14
18
|
start() {
|
|
15
19
|
this.stop();
|
|
20
|
+
// Load disabled list from config
|
|
21
|
+
let disabled = new Set();
|
|
22
|
+
try {
|
|
23
|
+
disabled = getConfigDisabledNames(loadConfig());
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// non-fatal — proceed with empty disabled set
|
|
27
|
+
}
|
|
16
28
|
let entries;
|
|
17
29
|
try {
|
|
18
30
|
entries = readdirSync(this.opts.recipesDir);
|
|
@@ -20,60 +32,220 @@ export class RecipeScheduler {
|
|
|
20
32
|
catch {
|
|
21
33
|
return [];
|
|
22
34
|
}
|
|
35
|
+
const candidates = [];
|
|
23
36
|
for (const f of entries) {
|
|
24
|
-
if (!f.endsWith(".json") || f.endsWith(".permissions.json"))
|
|
25
|
-
continue;
|
|
26
37
|
const fullPath = path.join(this.opts.recipesDir, f);
|
|
38
|
+
let isDir = false;
|
|
27
39
|
try {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
40
|
+
isDir = statSync(fullPath).isDirectory();
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (isDir) {
|
|
46
|
+
// Honor the per-recipe `.disabled` marker written by recipeInstall.
|
|
47
|
+
if (isInstallDirDisabled(fullPath)) {
|
|
48
|
+
this.opts.logger?.info?.(`[scheduler] skipping recipe in "${f}" — .disabled marker present`);
|
|
31
49
|
continue;
|
|
32
|
-
|
|
33
|
-
|
|
50
|
+
}
|
|
51
|
+
// Locate the recipe entrypoint inside the install dir.
|
|
52
|
+
// Prefer recipe.json's `recipes.main`, then fall back to first YAML.
|
|
53
|
+
const manifestPath = path.join(fullPath, "recipe.json");
|
|
54
|
+
let entrypoint = null;
|
|
55
|
+
if (existsSync(manifestPath)) {
|
|
56
|
+
try {
|
|
57
|
+
const m = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
58
|
+
if (m.recipes?.main) {
|
|
59
|
+
const candidate = path.join(fullPath, m.recipes.main);
|
|
60
|
+
if (existsSync(candidate))
|
|
61
|
+
entrypoint = candidate;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
// Malformed manifest — fall through to first-yaml fallback,
|
|
66
|
+
// but surface the issue so the user can fix it. Silent failures
|
|
67
|
+
// here led to "why isn't my recipe firing?" confusion.
|
|
68
|
+
this.opts.logger?.warn?.(`[scheduler] could not parse recipe.json in "${f}" — ${err instanceof Error ? err.message : String(err)}; falling back to first-yaml lookup`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!entrypoint) {
|
|
72
|
+
try {
|
|
73
|
+
const yaml = readdirSync(fullPath).find((x) => /\.ya?ml$/i.test(x));
|
|
74
|
+
if (yaml)
|
|
75
|
+
entrypoint = path.join(fullPath, yaml);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// unreadable — skip
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (entrypoint) {
|
|
82
|
+
const ext = path.extname(entrypoint).toLowerCase();
|
|
83
|
+
candidates.push({
|
|
84
|
+
filePath: entrypoint,
|
|
85
|
+
kind: ext === ".json" ? "json" : "yaml",
|
|
86
|
+
installDir: fullPath,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const isJson = f.endsWith(".json") && !f.endsWith(".permissions.json");
|
|
92
|
+
const isYaml = f.endsWith(".yaml") || f.endsWith(".yml");
|
|
93
|
+
if (!isJson && !isYaml)
|
|
94
|
+
continue;
|
|
95
|
+
candidates.push({
|
|
96
|
+
filePath: fullPath,
|
|
97
|
+
kind: isJson ? "json" : "yaml",
|
|
98
|
+
installDir: null,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
for (const cand of candidates) {
|
|
102
|
+
const f = path.basename(cand.filePath);
|
|
103
|
+
try {
|
|
104
|
+
let name;
|
|
105
|
+
let schedule;
|
|
106
|
+
if (cand.kind === "json") {
|
|
107
|
+
const raw = readFileSync(cand.filePath, "utf-8");
|
|
108
|
+
const parsed = JSON.parse(raw);
|
|
109
|
+
if (parsed.trigger?.type !== "cron")
|
|
110
|
+
continue;
|
|
111
|
+
if (!parsed.trigger.schedule ||
|
|
112
|
+
typeof parsed.trigger.schedule !== "string")
|
|
113
|
+
continue;
|
|
114
|
+
schedule = parsed.trigger.schedule;
|
|
115
|
+
name = parsed.name ?? path.basename(f, ".json");
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// YAML
|
|
119
|
+
const raw = readFileSync(cand.filePath, "utf-8");
|
|
120
|
+
const parsed = parseYaml(raw);
|
|
121
|
+
if (parsed.trigger?.type !== "cron")
|
|
122
|
+
continue;
|
|
123
|
+
schedule = parsed.trigger.at ?? parsed.trigger.schedule;
|
|
124
|
+
if (!schedule || typeof schedule !== "string")
|
|
125
|
+
continue;
|
|
126
|
+
name = parsed.name ?? path.basename(f, path.extname(f));
|
|
127
|
+
}
|
|
128
|
+
// Apply config-file disabled list (legacy mechanism)
|
|
129
|
+
if (disabled.has(name)) {
|
|
130
|
+
this.opts.logger?.info?.(`[scheduler] skipping disabled recipe "${name}"`);
|
|
34
131
|
continue;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
132
|
+
}
|
|
133
|
+
const parsed2 = parseSchedule(schedule);
|
|
134
|
+
if (parsed2 === null) {
|
|
135
|
+
this.opts.logger?.warn?.(`[scheduler] ignoring recipe "${name}" — unsupported schedule "${schedule}" (use @every Ns|Nm|Nh or a 5-field cron expression)`);
|
|
38
136
|
continue;
|
|
39
137
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
this.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
timer
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
138
|
+
if (parsed2.kind === "interval") {
|
|
139
|
+
const intervalMs = parsed2.intervalMs;
|
|
140
|
+
const timer = this.setIntervalFn(() => {
|
|
141
|
+
this.fire(name);
|
|
142
|
+
}, intervalMs);
|
|
143
|
+
if (typeof timer === "object" && "unref" in timer)
|
|
144
|
+
timer.unref();
|
|
145
|
+
this.scheduled.push({
|
|
146
|
+
name,
|
|
147
|
+
schedule,
|
|
148
|
+
intervalMs,
|
|
149
|
+
timer,
|
|
150
|
+
});
|
|
151
|
+
this.opts.logger?.info?.(`[scheduler] "${name}" scheduled every ${intervalMs}ms (${schedule})`);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// cron5
|
|
155
|
+
const cronJob = cron.schedule(parsed2.expression, () => {
|
|
156
|
+
this.fire(name);
|
|
157
|
+
});
|
|
158
|
+
// Store a sentinel timer so the ScheduledRecipe shape stays stable
|
|
159
|
+
const dummyTimer = this.setIntervalFn(() => { }, 2_147_483_647);
|
|
160
|
+
if (typeof dummyTimer === "object" && "unref" in dummyTimer)
|
|
161
|
+
dummyTimer.unref();
|
|
162
|
+
this.scheduled.push({
|
|
163
|
+
name,
|
|
164
|
+
schedule,
|
|
165
|
+
intervalMs: 0,
|
|
166
|
+
timer: dummyTimer,
|
|
167
|
+
cronJob,
|
|
168
|
+
});
|
|
169
|
+
this.opts.logger?.info?.(`[scheduler] "${name}" scheduled with cron expression "${schedule}"`);
|
|
170
|
+
}
|
|
53
171
|
}
|
|
54
|
-
catch {
|
|
55
|
-
//
|
|
172
|
+
catch (err) {
|
|
173
|
+
// Malformed recipe file — surface so users can debug rather than
|
|
174
|
+
// silently dropping the recipe from the schedule.
|
|
175
|
+
this.opts.logger?.warn?.(`[scheduler] could not load recipe at "${cand.filePath}" — ${err instanceof Error ? err.message : String(err)}; recipe will not be scheduled`);
|
|
56
176
|
}
|
|
57
177
|
}
|
|
58
178
|
return this.scheduled;
|
|
59
179
|
}
|
|
60
180
|
stop() {
|
|
61
181
|
for (const entry of this.scheduled) {
|
|
62
|
-
|
|
182
|
+
if (entry.cronJob) {
|
|
183
|
+
entry.cronJob.stop();
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
this.clearIntervalFn(entry.timer);
|
|
187
|
+
}
|
|
63
188
|
}
|
|
64
189
|
this.scheduled = [];
|
|
65
190
|
}
|
|
191
|
+
restart() {
|
|
192
|
+
this.stop();
|
|
193
|
+
this.start();
|
|
194
|
+
}
|
|
66
195
|
list() {
|
|
67
|
-
return this.scheduled.map(({ timer: _t, ...rest }) => rest);
|
|
196
|
+
return this.scheduled.map(({ timer: _t, cronJob: _c, ...rest }) => rest);
|
|
68
197
|
}
|
|
69
198
|
/** Test hook: dispatch a recipe immediately without waiting for the interval. */
|
|
70
199
|
fireForTest(name) {
|
|
71
200
|
this.fire(name);
|
|
72
201
|
}
|
|
73
202
|
fire(name) {
|
|
203
|
+
// TOCTOU defence: re-check the legacy `cfg.recipes.disabled` list at
|
|
204
|
+
// fire time. `start()` snapshots it once; if the user runs `recipe
|
|
205
|
+
// disable <name>` after start (and the recipe is a top-level legacy
|
|
206
|
+
// file, where the marker file doesn't apply), the timer would
|
|
207
|
+
// otherwise still fire until next restart(). The `.disabled` marker
|
|
208
|
+
// case is handled inside findYamlRecipePath / loadRecipePrompt
|
|
209
|
+
// (skip disabled install dirs) thanks to PR #49.
|
|
210
|
+
try {
|
|
211
|
+
if (getConfigDisabledNames(loadConfig()).has(name)) {
|
|
212
|
+
this.opts.logger?.info?.(`[scheduler] skipping "${name}" — disabled via config (TOCTOU re-check)`);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// proceed if config unreadable — falling back to scan-time snapshot
|
|
218
|
+
}
|
|
219
|
+
// YAML recipe — delegate to runYaml if provided. findYamlRecipePath
|
|
220
|
+
// now throws RecipeNameConflictError when two recipes declare the same
|
|
221
|
+
// name; surface that loudly instead of letting the timer crash silently.
|
|
222
|
+
let yamlPath;
|
|
223
|
+
try {
|
|
224
|
+
yamlPath = findYamlRecipePath(this.opts.recipesDir, name);
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
this.opts.logger?.warn?.(`[scheduler] skipped "${name}" — ${err instanceof Error ? err.message : String(err)}`);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (yamlPath) {
|
|
231
|
+
if (!this.opts.runYaml) {
|
|
232
|
+
this.opts.logger?.warn?.(`[scheduler] skipped "${name}" — YAML recipe requires runYaml callback (start bridge with --claude-driver)`);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
this.opts.runYaml(name).catch((err) => {
|
|
236
|
+
this.opts.logger?.warn?.(`[scheduler] YAML recipe "${name}" failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
237
|
+
});
|
|
238
|
+
this.opts.logger?.info?.(`[scheduler] fired YAML recipe "${name}"`);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
// JSON recipe — legacy path
|
|
74
242
|
const loaded = loadRecipePrompt(this.opts.recipesDir, name);
|
|
75
243
|
if (!loaded) {
|
|
76
|
-
|
|
244
|
+
// After PR #49, findYamlRecipePath / loadRecipePrompt return null for
|
|
245
|
+
// recipes whose install dir has a `.disabled` marker — that's the
|
|
246
|
+
// common case here. "Disappeared" was misleading; prefer a message
|
|
247
|
+
// that names both possibilities.
|
|
248
|
+
this.opts.logger?.warn?.(`[scheduler] skipped "${name}" — recipe not found or disabled`);
|
|
77
249
|
return;
|
|
78
250
|
}
|
|
79
251
|
try {
|
|
@@ -88,23 +260,31 @@ export class RecipeScheduler {
|
|
|
88
260
|
}
|
|
89
261
|
}
|
|
90
262
|
}
|
|
91
|
-
/** Parse @every forms into milliseconds. Returns null for unsupported schedules. */
|
|
263
|
+
/** Parse @every forms into milliseconds, or detect a 5-field cron expression. Returns null for unsupported schedules. */
|
|
92
264
|
export function parseSchedule(schedule) {
|
|
93
265
|
const trimmed = schedule.trim();
|
|
266
|
+
// @every Ns|Nm|Nh
|
|
94
267
|
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
|
-
|
|
268
|
+
if (m) {
|
|
269
|
+
const n = Number.parseInt(m[1], 10);
|
|
270
|
+
const unit = m[2]?.toLowerCase();
|
|
271
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
272
|
+
return null;
|
|
273
|
+
const multiplier = unit === "ms"
|
|
274
|
+
? 1
|
|
275
|
+
: unit === "s"
|
|
276
|
+
? 1000
|
|
277
|
+
: unit === "m"
|
|
278
|
+
? 60_000
|
|
279
|
+
: 60 * 60_000;
|
|
280
|
+
return { kind: "interval", intervalMs: n * multiplier };
|
|
281
|
+
}
|
|
282
|
+
// 5-field cron expression (e.g. "0 8 * * 1-5")
|
|
283
|
+
if (/^\S+\s+\S+\s+\S+\s+\S+\s+\S+$/.test(trimmed)) {
|
|
284
|
+
if (cron.validate(trimmed)) {
|
|
285
|
+
return { kind: "cron5", expression: trimmed };
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
109
289
|
}
|
|
110
290
|
//# 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;
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/recipes/scheduler.ts"],"names":[],"mappings":"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"}
|